goscript 0.1.0 → 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 (495) hide show
  1. package/README.md +267 -255
  2. package/cmd/goscript/cmd-test.go +286 -0
  3. package/cmd/goscript/cmd-test_test.go +76 -0
  4. package/cmd/goscript/cmd_compile.go +9 -0
  5. package/cmd/goscript/main.go +1 -0
  6. package/compiler/build-flags.go +38 -0
  7. package/compiler/compile-request.go +33 -0
  8. package/compiler/compiler.go +1 -1
  9. package/compiler/compliance_test.go +0 -10
  10. package/compiler/config.go +2 -0
  11. package/compiler/gotest/owner.go +24 -0
  12. package/compiler/gotest/package-result.go +69 -0
  13. package/compiler/gotest/request.go +210 -0
  14. package/compiler/gotest/result.go +28 -0
  15. package/compiler/gotest/runner.go +1225 -0
  16. package/compiler/gotest/runner_test.go +1271 -0
  17. package/compiler/gotest/test.go +9 -0
  18. package/compiler/index.test.ts +1 -1
  19. package/compiler/lowered-program.go +80 -21
  20. package/compiler/lowering.go +6754 -602
  21. package/compiler/override-facts.go +357 -0
  22. package/compiler/override-registry.go +52 -190
  23. package/compiler/override-registry_test.go +182 -0
  24. package/compiler/package-graph.go +50 -27
  25. package/compiler/package-graph_test.go +99 -9
  26. package/compiler/package-test-function.go +9 -0
  27. package/compiler/package-test-graph-package.go +40 -0
  28. package/compiler/package-test-graph-variant.go +129 -0
  29. package/compiler/package-test-graph.go +112 -0
  30. package/compiler/package-test-graph_test.go +202 -0
  31. package/compiler/runtime-contract.go +229 -29
  32. package/compiler/runtime-contract_test.go +44 -30
  33. package/compiler/semantic-model-types.go +25 -6
  34. package/compiler/semantic-model.go +819 -74
  35. package/compiler/semantic-model_test.go +104 -0
  36. package/compiler/service.go +10 -4
  37. package/compiler/skeleton_test.go +2777 -524
  38. package/compiler/tsworkspace/owner-process-unix_test.go +72 -0
  39. package/compiler/tsworkspace/owner.go +342 -0
  40. package/compiler/tsworkspace/owner_test.go +93 -0
  41. package/compiler/tsworkspace/result.go +17 -0
  42. package/compiler/tsworkspace/tool-process-other.go +14 -0
  43. package/compiler/tsworkspace/tool-process-unix.go +19 -0
  44. package/compiler/typescript-emitter.go +576 -86
  45. package/compiler/wasm/compile.go +1 -1
  46. package/compiler/wasm/compile_test.go +61 -11
  47. package/compiler/wasm_api.go +172 -7
  48. package/dist/gs/builtin/builtin.d.ts +40 -3
  49. package/dist/gs/builtin/builtin.js +430 -22
  50. package/dist/gs/builtin/builtin.js.map +1 -1
  51. package/dist/gs/builtin/channel.d.ts +32 -10
  52. package/dist/gs/builtin/channel.js +119 -25
  53. package/dist/gs/builtin/channel.js.map +1 -1
  54. package/dist/gs/builtin/defer.d.ts +1 -0
  55. package/dist/gs/builtin/defer.js +12 -2
  56. package/dist/gs/builtin/defer.js.map +1 -1
  57. package/dist/gs/builtin/hostio.d.ts +9 -0
  58. package/dist/gs/builtin/hostio.js +25 -0
  59. package/dist/gs/builtin/hostio.js.map +1 -1
  60. package/dist/gs/builtin/map.js +40 -6
  61. package/dist/gs/builtin/map.js.map +1 -1
  62. package/dist/gs/builtin/print.js.map +1 -1
  63. package/dist/gs/builtin/slice.d.ts +64 -10
  64. package/dist/gs/builtin/slice.js +619 -244
  65. package/dist/gs/builtin/slice.js.map +1 -1
  66. package/dist/gs/builtin/type.d.ts +7 -2
  67. package/dist/gs/builtin/type.js +128 -29
  68. package/dist/gs/builtin/type.js.map +1 -1
  69. package/dist/gs/builtin/varRef.d.ts +7 -0
  70. package/dist/gs/builtin/varRef.js +23 -0
  71. package/dist/gs/builtin/varRef.js.map +1 -1
  72. package/dist/gs/bytes/buffer.gs.js +74 -70
  73. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  74. package/dist/gs/bytes/iter.gs.js +13 -13
  75. package/dist/gs/bytes/iter.gs.js.map +1 -1
  76. package/dist/gs/bytes/reader.gs.js +20 -18
  77. package/dist/gs/bytes/reader.gs.js.map +1 -1
  78. package/dist/gs/compress/zlib/index.d.ts +26 -0
  79. package/dist/gs/compress/zlib/index.js +168 -0
  80. package/dist/gs/compress/zlib/index.js.map +1 -0
  81. package/dist/gs/context/context.d.ts +6 -5
  82. package/dist/gs/context/context.js +17 -12
  83. package/dist/gs/context/context.js.map +1 -1
  84. package/dist/gs/crypto/ecdh/index.d.ts +52 -0
  85. package/dist/gs/crypto/ecdh/index.js +226 -0
  86. package/dist/gs/crypto/ecdh/index.js.map +1 -0
  87. package/dist/gs/crypto/ed25519/index.d.ts +34 -0
  88. package/dist/gs/crypto/ed25519/index.js +160 -0
  89. package/dist/gs/crypto/ed25519/index.js.map +1 -0
  90. package/dist/gs/crypto/internal/constanttime/index.d.ts +4 -0
  91. package/dist/gs/crypto/internal/constanttime/index.js +18 -0
  92. package/dist/gs/crypto/internal/constanttime/index.js.map +1 -0
  93. package/dist/gs/crypto/internal/fips140deps/byteorder/index.d.ts +1 -0
  94. package/dist/gs/crypto/internal/fips140deps/byteorder/index.js +2 -0
  95. package/dist/gs/crypto/internal/fips140deps/byteorder/index.js.map +1 -0
  96. package/dist/gs/crypto/internal/fips140deps/godebug/index.d.ts +1 -0
  97. package/dist/gs/crypto/internal/fips140deps/godebug/index.js +2 -0
  98. package/dist/gs/crypto/internal/fips140deps/godebug/index.js.map +1 -0
  99. package/dist/gs/crypto/rand/index.d.ts +2 -0
  100. package/dist/gs/crypto/rand/index.js +85 -0
  101. package/dist/gs/crypto/rand/index.js.map +1 -1
  102. package/dist/gs/crypto/sha256/index.d.ts +8 -0
  103. package/dist/gs/crypto/sha256/index.js +118 -0
  104. package/dist/gs/crypto/sha256/index.js.map +1 -0
  105. package/dist/gs/crypto/sha512/index.d.ts +14 -0
  106. package/dist/gs/crypto/sha512/index.js +129 -0
  107. package/dist/gs/crypto/sha512/index.js.map +1 -0
  108. package/dist/gs/embed/index.d.ts +7 -0
  109. package/dist/gs/embed/index.js +16 -0
  110. package/dist/gs/embed/index.js.map +1 -0
  111. package/dist/gs/encoding/json/index.d.ts +4 -0
  112. package/dist/gs/encoding/json/index.js +33 -0
  113. package/dist/gs/encoding/json/index.js.map +1 -1
  114. package/dist/gs/errors/errors.d.ts +4 -0
  115. package/dist/gs/errors/errors.js +108 -4
  116. package/dist/gs/errors/errors.js.map +1 -1
  117. package/dist/gs/fmt/fmt.d.ts +4 -4
  118. package/dist/gs/fmt/fmt.js +42 -11
  119. package/dist/gs/fmt/fmt.js.map +1 -1
  120. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +37 -2
  121. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +245 -1
  122. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  123. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +189 -0
  124. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +861 -0
  125. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -0
  126. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +217 -0
  127. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +814 -0
  128. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -0
  129. package/dist/gs/github.com/aperturerobotics/util/conc/index.d.ts +20 -0
  130. package/dist/gs/github.com/aperturerobotics/util/conc/index.js +134 -0
  131. package/dist/gs/github.com/aperturerobotics/util/conc/index.js.map +1 -0
  132. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  133. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.d.ts +3 -0
  134. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.js +50 -0
  135. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.js.map +1 -0
  136. package/dist/gs/github.com/klauspost/compress/internal/le/index.d.ts +9 -0
  137. package/dist/gs/github.com/klauspost/compress/internal/le/index.js +72 -0
  138. package/dist/gs/github.com/klauspost/compress/internal/le/index.js.map +1 -0
  139. package/dist/gs/github.com/mr-tron/base58/base58/index.d.ts +27 -0
  140. package/dist/gs/github.com/mr-tron/base58/base58/index.js +172 -0
  141. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -0
  142. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.d.ts +21 -0
  143. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.js +22 -0
  144. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.js.map +1 -0
  145. package/dist/gs/go/internal/scannerhooks/index.d.ts +3 -0
  146. package/dist/gs/go/internal/scannerhooks/index.js +5 -0
  147. package/dist/gs/go/internal/scannerhooks/index.js.map +1 -0
  148. package/dist/gs/go/scanner/index.d.ts +13 -0
  149. package/dist/gs/go/scanner/index.js +35 -0
  150. package/dist/gs/go/scanner/index.js.map +1 -1
  151. package/dist/gs/go/token/index.d.ts +156 -0
  152. package/dist/gs/go/token/index.js +507 -4
  153. package/dist/gs/go/token/index.js.map +1 -1
  154. package/dist/gs/hash/fnv/index.d.ts +57 -0
  155. package/dist/gs/hash/fnv/index.js +299 -0
  156. package/dist/gs/hash/fnv/index.js.map +1 -0
  157. package/dist/gs/hash/index.d.ts +17 -0
  158. package/dist/gs/hash/index.js +94 -0
  159. package/dist/gs/hash/index.js.map +1 -0
  160. package/dist/gs/internal/abi/index.d.ts +4 -0
  161. package/dist/gs/internal/abi/index.js +10 -0
  162. package/dist/gs/internal/abi/index.js.map +1 -1
  163. package/dist/gs/internal/bytealg/index.d.ts +2 -0
  164. package/dist/gs/internal/bytealg/index.js +14 -0
  165. package/dist/gs/internal/bytealg/index.js.map +1 -1
  166. package/dist/gs/internal/byteorder/index.d.ts +8 -2
  167. package/dist/gs/internal/byteorder/index.js +56 -25
  168. package/dist/gs/internal/byteorder/index.js.map +1 -1
  169. package/dist/gs/internal/godebug/index.d.ts +12 -0
  170. package/dist/gs/internal/godebug/index.js +30 -0
  171. package/dist/gs/internal/godebug/index.js.map +1 -0
  172. package/dist/gs/io/fs/index.d.ts +1 -0
  173. package/dist/gs/io/fs/index.js +1 -0
  174. package/dist/gs/io/fs/index.js.map +1 -1
  175. package/dist/gs/io/fs/readlink.d.ts +8 -0
  176. package/dist/gs/io/fs/readlink.js +60 -0
  177. package/dist/gs/io/fs/readlink.js.map +1 -0
  178. package/dist/gs/io/fs/walk.d.ts +3 -3
  179. package/dist/gs/io/fs/walk.js +7 -7
  180. package/dist/gs/io/fs/walk.js.map +1 -1
  181. package/dist/gs/io/io.d.ts +40 -6
  182. package/dist/gs/io/io.js +151 -26
  183. package/dist/gs/io/io.js.map +1 -1
  184. package/dist/gs/iter/iter.d.ts +3 -2
  185. package/dist/gs/iter/iter.js.map +1 -1
  186. package/dist/gs/maps/iter.d.ts +5 -5
  187. package/dist/gs/maps/iter.js +48 -21
  188. package/dist/gs/maps/iter.js.map +1 -1
  189. package/dist/gs/maps/maps.d.ts +6 -6
  190. package/dist/gs/maps/maps.js +1 -1
  191. package/dist/gs/maps/maps.js.map +1 -1
  192. package/dist/gs/math/bits/index.d.ts +13 -4
  193. package/dist/gs/math/bits/index.js +70 -48
  194. package/dist/gs/math/bits/index.js.map +1 -1
  195. package/dist/gs/math/const.gs.d.ts +5 -5
  196. package/dist/gs/math/const.gs.js +4 -4
  197. package/dist/gs/math/const.gs.js.map +1 -1
  198. package/dist/gs/mime/index.d.ts +1 -0
  199. package/dist/gs/mime/index.js +52 -0
  200. package/dist/gs/mime/index.js.map +1 -0
  201. package/dist/gs/net/http/httptest/index.d.ts +30 -0
  202. package/dist/gs/net/http/httptest/index.js +101 -0
  203. package/dist/gs/net/http/httptest/index.js.map +1 -0
  204. package/dist/gs/net/http/index.d.ts +131 -0
  205. package/dist/gs/net/http/index.js +307 -0
  206. package/dist/gs/net/http/index.js.map +1 -0
  207. package/dist/gs/net/http/pprof/index.d.ts +8 -0
  208. package/dist/gs/net/http/pprof/index.js +59 -0
  209. package/dist/gs/net/http/pprof/index.js.map +1 -0
  210. package/dist/gs/os/dir_unix.gs.js +2 -2
  211. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  212. package/dist/gs/os/error.gs.js +9 -7
  213. package/dist/gs/os/error.gs.js.map +1 -1
  214. package/dist/gs/os/types_js.gs.js +95 -15
  215. package/dist/gs/os/types_js.gs.js.map +1 -1
  216. package/dist/gs/path/filepath/match.js +165 -3
  217. package/dist/gs/path/filepath/match.js.map +1 -1
  218. package/dist/gs/path/filepath/path.d.ts +8 -4
  219. package/dist/gs/path/filepath/path.js +192 -8
  220. package/dist/gs/path/filepath/path.js.map +1 -1
  221. package/dist/gs/path/path.d.ts +4 -1
  222. package/dist/gs/path/path.js +16 -4
  223. package/dist/gs/path/path.js.map +1 -1
  224. package/dist/gs/reflect/index.d.ts +4 -3
  225. package/dist/gs/reflect/index.js +3 -2
  226. package/dist/gs/reflect/index.js.map +1 -1
  227. package/dist/gs/reflect/iter.js +2 -2
  228. package/dist/gs/reflect/iter.js.map +1 -1
  229. package/dist/gs/reflect/map.js +29 -0
  230. package/dist/gs/reflect/map.js.map +1 -1
  231. package/dist/gs/reflect/type.d.ts +31 -9
  232. package/dist/gs/reflect/type.js +536 -43
  233. package/dist/gs/reflect/type.js.map +1 -1
  234. package/dist/gs/reflect/types.d.ts +1 -0
  235. package/dist/gs/reflect/types.js +3 -1
  236. package/dist/gs/reflect/types.js.map +1 -1
  237. package/dist/gs/reflect/value.d.ts +4 -1
  238. package/dist/gs/reflect/value.js +39 -1
  239. package/dist/gs/reflect/value.js.map +1 -1
  240. package/dist/gs/reflect/visiblefields.js +1 -1
  241. package/dist/gs/reflect/visiblefields.js.map +1 -1
  242. package/dist/gs/runtime/debug/index.d.ts +41 -0
  243. package/dist/gs/runtime/debug/index.js +66 -0
  244. package/dist/gs/runtime/debug/index.js.map +1 -0
  245. package/dist/gs/runtime/pprof/index.d.ts +20 -0
  246. package/dist/gs/runtime/pprof/index.js +85 -0
  247. package/dist/gs/runtime/pprof/index.js.map +1 -0
  248. package/dist/gs/runtime/runtime.d.ts +35 -3
  249. package/dist/gs/runtime/runtime.js +72 -0
  250. package/dist/gs/runtime/runtime.js.map +1 -1
  251. package/dist/gs/runtime/trace/index.d.ts +19 -0
  252. package/dist/gs/runtime/trace/index.js +64 -0
  253. package/dist/gs/runtime/trace/index.js.map +1 -0
  254. package/dist/gs/slices/slices.d.ts +42 -8
  255. package/dist/gs/slices/slices.js +425 -11
  256. package/dist/gs/slices/slices.js.map +1 -1
  257. package/dist/gs/sort/slice.gs.d.ts +5 -3
  258. package/dist/gs/sort/slice.gs.js +60 -22
  259. package/dist/gs/sort/slice.gs.js.map +1 -1
  260. package/dist/gs/sort/sort.gs.d.ts +4 -4
  261. package/dist/gs/sort/sort.gs.js +11 -8
  262. package/dist/gs/sort/sort.gs.js.map +1 -1
  263. package/dist/gs/strings/builder.d.ts +1 -1
  264. package/dist/gs/strings/builder.js +29 -19
  265. package/dist/gs/strings/builder.js.map +1 -1
  266. package/dist/gs/strings/iter.js +140 -75
  267. package/dist/gs/strings/iter.js.map +1 -1
  268. package/dist/gs/strings/replace.js +2 -2
  269. package/dist/gs/strings/replace.js.map +1 -1
  270. package/dist/gs/strings/strings.js +52 -6
  271. package/dist/gs/strings/strings.js.map +1 -1
  272. package/dist/gs/sync/atomic/type.gs.d.ts +9 -8
  273. package/dist/gs/sync/atomic/type.gs.js +0 -2
  274. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  275. package/dist/gs/sync/sync.d.ts +8 -3
  276. package/dist/gs/sync/sync.js +66 -11
  277. package/dist/gs/sync/sync.js.map +1 -1
  278. package/dist/gs/syscall/constants.d.ts +36 -24
  279. package/dist/gs/syscall/constants.js +12 -0
  280. package/dist/gs/syscall/constants.js.map +1 -1
  281. package/dist/gs/syscall/errors.d.ts +117 -111
  282. package/dist/gs/syscall/errors.js +45 -0
  283. package/dist/gs/syscall/errors.js.map +1 -1
  284. package/dist/gs/syscall/fs.d.ts +37 -0
  285. package/dist/gs/syscall/fs.js +102 -0
  286. package/dist/gs/syscall/fs.js.map +1 -1
  287. package/dist/gs/syscall/js/index.d.ts +90 -0
  288. package/dist/gs/syscall/js/index.js +383 -0
  289. package/dist/gs/syscall/js/index.js.map +1 -0
  290. package/dist/gs/syscall/types.d.ts +26 -1
  291. package/dist/gs/syscall/types.js +45 -1
  292. package/dist/gs/syscall/types.js.map +1 -1
  293. package/dist/gs/testing/index.d.ts +1 -0
  294. package/dist/gs/testing/index.js +2 -0
  295. package/dist/gs/testing/index.js.map +1 -0
  296. package/dist/gs/testing/testing.d.ts +78 -0
  297. package/dist/gs/testing/testing.js +318 -0
  298. package/dist/gs/testing/testing.js.map +1 -0
  299. package/dist/gs/time/time.d.ts +41 -4
  300. package/dist/gs/time/time.js +227 -36
  301. package/dist/gs/time/time.js.map +1 -1
  302. package/dist/gs/unicode/unicode.d.ts +23 -1
  303. package/dist/gs/unicode/unicode.js +79 -10
  304. package/dist/gs/unicode/unicode.js.map +1 -1
  305. package/dist/gs/unicode/utf8/utf8.d.ts +4 -4
  306. package/dist/gs/unicode/utf8/utf8.js +24 -11
  307. package/dist/gs/unicode/utf8/utf8.js.map +1 -1
  308. package/dist/gs/unique/index.d.ts +11 -0
  309. package/dist/gs/unique/index.js +76 -0
  310. package/dist/gs/unique/index.js.map +1 -0
  311. package/go.mod +8 -8
  312. package/go.sum +14 -14
  313. package/gs/builtin/builtin.ts +585 -27
  314. package/gs/builtin/channel.ts +183 -29
  315. package/gs/builtin/defer.ts +13 -2
  316. package/gs/builtin/hostio.test.ts +1 -0
  317. package/gs/builtin/hostio.ts +38 -0
  318. package/gs/builtin/map.ts +46 -6
  319. package/gs/builtin/print.ts +12 -3
  320. package/gs/builtin/runtime-contract.test.ts +383 -10
  321. package/gs/builtin/slice.test.ts +70 -0
  322. package/gs/builtin/slice.ts +785 -265
  323. package/gs/builtin/type.ts +160 -41
  324. package/gs/builtin/varRef.ts +40 -1
  325. package/gs/bytes/buffer.gs.ts +74 -70
  326. package/gs/bytes/iter.gs.ts +13 -14
  327. package/gs/bytes/meta.json +8 -3
  328. package/gs/bytes/reader.gs.ts +20 -19
  329. package/gs/compress/zlib/index.test.ts +28 -0
  330. package/gs/compress/zlib/index.ts +200 -0
  331. package/gs/compress/zlib/meta.json +3 -0
  332. package/gs/context/context.test.ts +71 -0
  333. package/gs/context/context.ts +30 -29
  334. package/gs/crypto/ecdh/index.test.ts +43 -0
  335. package/gs/crypto/ecdh/index.ts +274 -0
  336. package/gs/crypto/ed25519/index.test.ts +41 -0
  337. package/gs/crypto/ed25519/index.ts +238 -0
  338. package/gs/crypto/ed25519/meta.json +13 -0
  339. package/gs/crypto/internal/constanttime/index.test.ts +25 -0
  340. package/gs/crypto/internal/constanttime/index.ts +22 -0
  341. package/gs/crypto/internal/fips140deps/byteorder/index.ts +1 -0
  342. package/gs/crypto/internal/fips140deps/godebug/index.ts +1 -0
  343. package/gs/crypto/rand/index.test.ts +89 -1
  344. package/gs/crypto/rand/index.ts +103 -1
  345. package/gs/crypto/rand/meta.json +4 -1
  346. package/gs/crypto/sha256/index.test.ts +78 -0
  347. package/gs/crypto/sha256/index.ts +150 -0
  348. package/gs/crypto/sha256/meta.json +9 -0
  349. package/gs/crypto/sha512/index.test.ts +31 -0
  350. package/gs/crypto/sha512/index.ts +161 -0
  351. package/gs/crypto/sha512/meta.json +11 -0
  352. package/gs/embed/index.ts +20 -0
  353. package/gs/embed/meta.json +5 -0
  354. package/gs/encoding/json/index.test.ts +39 -3
  355. package/gs/encoding/json/index.ts +45 -3
  356. package/gs/errors/errors.test.ts +85 -0
  357. package/gs/errors/errors.ts +132 -4
  358. package/gs/fmt/fmt.test.ts +3 -1
  359. package/gs/fmt/fmt.ts +55 -19
  360. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +128 -1
  361. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +342 -4
  362. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +180 -0
  363. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +1084 -0
  364. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +31 -0
  365. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +1233 -0
  366. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +46 -0
  367. package/gs/github.com/aperturerobotics/util/conc/index.test.ts +30 -0
  368. package/gs/github.com/aperturerobotics/util/conc/index.ts +172 -0
  369. package/gs/github.com/aperturerobotics/util/conc/meta.json +9 -0
  370. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.ts +1 -4
  371. package/gs/github.com/hack-pad/safejs/internal/catch/index.test.ts +35 -0
  372. package/gs/github.com/hack-pad/safejs/internal/catch/index.ts +65 -0
  373. package/gs/github.com/hack-pad/safejs/internal/catch/meta.json +9 -0
  374. package/gs/github.com/klauspost/compress/internal/le/index.test.ts +37 -0
  375. package/gs/github.com/klauspost/compress/internal/le/index.ts +115 -0
  376. package/gs/github.com/mr-tron/base58/base58/index.test.ts +70 -0
  377. package/gs/github.com/mr-tron/base58/base58/index.ts +231 -0
  378. package/gs/github.com/mr-tron/base58/base58/meta.json +3 -0
  379. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +46 -0
  380. package/gs/github.com/zeebo/blake3/internal/consts/index.ts +26 -0
  381. package/gs/go/internal/scannerhooks/index.test.ts +14 -0
  382. package/gs/go/internal/scannerhooks/index.ts +9 -0
  383. package/gs/go/scanner/index.test.ts +22 -0
  384. package/gs/go/scanner/index.ts +47 -0
  385. package/gs/go/token/index.test.ts +47 -1
  386. package/gs/go/token/index.ts +583 -4
  387. package/gs/hash/fnv/index.test.ts +67 -0
  388. package/gs/hash/fnv/index.ts +351 -0
  389. package/gs/hash/fnv/meta.json +3 -0
  390. package/gs/hash/index.test.ts +37 -0
  391. package/gs/hash/index.ts +118 -0
  392. package/gs/hash/meta.json +5 -0
  393. package/gs/internal/abi/index.test.ts +18 -0
  394. package/gs/internal/abi/index.ts +14 -0
  395. package/gs/internal/bytealg/index.test.ts +18 -0
  396. package/gs/internal/bytealg/index.ts +16 -0
  397. package/gs/internal/byteorder/index.test.ts +39 -0
  398. package/gs/internal/byteorder/index.ts +100 -27
  399. package/gs/internal/godebug/index.test.ts +16 -0
  400. package/gs/internal/godebug/index.ts +35 -0
  401. package/gs/io/fs/index.ts +1 -0
  402. package/gs/io/fs/meta.json +5 -0
  403. package/gs/io/fs/readlink.test.ts +43 -0
  404. package/gs/io/fs/readlink.ts +69 -0
  405. package/gs/io/fs/walk.test.ts +61 -0
  406. package/gs/io/fs/walk.ts +17 -9
  407. package/gs/io/io.ts +177 -31
  408. package/gs/io/meta.json +10 -2
  409. package/gs/iter/iter.ts +8 -2
  410. package/gs/maps/iter.ts +75 -26
  411. package/gs/maps/maps.test.ts +23 -0
  412. package/gs/maps/maps.ts +13 -11
  413. package/gs/math/bits/index.test.ts +20 -0
  414. package/gs/math/bits/index.ts +107 -64
  415. package/gs/math/const.gs.test.ts +11 -5
  416. package/gs/math/const.gs.ts +5 -6
  417. package/gs/mime/index.ts +60 -0
  418. package/gs/net/http/httptest/index.test.ts +53 -0
  419. package/gs/net/http/httptest/index.ts +120 -0
  420. package/gs/net/http/index.test.ts +148 -0
  421. package/gs/net/http/index.ts +432 -0
  422. package/gs/net/http/meta.json +6 -0
  423. package/gs/net/http/pprof/index.test.ts +47 -0
  424. package/gs/net/http/pprof/index.ts +65 -0
  425. package/gs/os/dir_unix.gs.ts +2 -3
  426. package/gs/os/error.gs.ts +9 -10
  427. package/gs/os/error.test.ts +41 -0
  428. package/gs/os/file_unix_js.test.ts +55 -0
  429. package/gs/os/tempfile.gs.test.ts +37 -10
  430. package/gs/os/types_js.gs.ts +96 -17
  431. package/gs/path/filepath/match.test.ts +31 -12
  432. package/gs/path/filepath/match.ts +181 -3
  433. package/gs/path/filepath/meta.json +6 -0
  434. package/gs/path/filepath/path.test.ts +80 -0
  435. package/gs/path/filepath/path.ts +244 -11
  436. package/gs/path/path.ts +20 -5
  437. package/gs/reflect/field.test.ts +63 -0
  438. package/gs/reflect/index.ts +5 -1
  439. package/gs/reflect/iter.ts +2 -2
  440. package/gs/reflect/map.test.ts +42 -1
  441. package/gs/reflect/map.ts +39 -0
  442. package/gs/reflect/type.ts +728 -65
  443. package/gs/reflect/typefor.test.ts +100 -0
  444. package/gs/reflect/types.ts +3 -1
  445. package/gs/reflect/value.ts +50 -1
  446. package/gs/reflect/visiblefields.ts +1 -1
  447. package/gs/runtime/debug/index.test.ts +45 -0
  448. package/gs/runtime/debug/index.ts +96 -0
  449. package/gs/runtime/pprof/index.test.ts +36 -0
  450. package/gs/runtime/pprof/index.ts +104 -0
  451. package/gs/runtime/pprof/meta.json +6 -0
  452. package/gs/runtime/runtime.test.ts +19 -0
  453. package/gs/runtime/runtime.ts +98 -3
  454. package/gs/runtime/trace/index.test.ts +45 -0
  455. package/gs/runtime/trace/index.ts +97 -0
  456. package/gs/runtime/trace/meta.json +7 -0
  457. package/gs/slices/meta.json +2 -1
  458. package/gs/slices/slices.test.ts +180 -0
  459. package/gs/slices/slices.ts +502 -15
  460. package/gs/sort/meta.json +7 -0
  461. package/gs/sort/slice.gs.ts +85 -26
  462. package/gs/sort/slice.test.ts +40 -0
  463. package/gs/sort/sort.gs.ts +16 -13
  464. package/gs/strings/builder.test.ts +8 -0
  465. package/gs/strings/builder.ts +33 -20
  466. package/gs/strings/iter.test.ts +5 -7
  467. package/gs/strings/iter.ts +146 -71
  468. package/gs/strings/replace.test.ts +1 -4
  469. package/gs/strings/replace.ts +6 -6
  470. package/gs/strings/strings.test.ts +4 -0
  471. package/gs/strings/strings.ts +54 -6
  472. package/gs/sync/atomic/type.gs.ts +13 -14
  473. package/gs/sync/meta.json +3 -1
  474. package/gs/sync/sync.test.ts +69 -1
  475. package/gs/sync/sync.ts +72 -13
  476. package/gs/syscall/constants.ts +39 -24
  477. package/gs/syscall/errors.ts +165 -112
  478. package/gs/syscall/fs.ts +195 -0
  479. package/gs/syscall/js/index.ts +485 -0
  480. package/gs/syscall/js/meta.json +4 -0
  481. package/gs/syscall/net.test.ts +111 -0
  482. package/gs/syscall/types.ts +63 -2
  483. package/gs/testing/index.ts +1 -0
  484. package/gs/testing/meta.json +5 -0
  485. package/gs/testing/testing.test.ts +146 -0
  486. package/gs/testing/testing.ts +399 -0
  487. package/gs/time/meta.json +2 -2
  488. package/gs/time/time.test.ts +110 -0
  489. package/gs/time/time.ts +309 -57
  490. package/gs/unicode/unicode.test.ts +36 -0
  491. package/gs/unicode/unicode.ts +115 -9
  492. package/gs/unicode/utf8/utf8.test.ts +13 -0
  493. package/gs/unicode/utf8/utf8.ts +28 -16
  494. package/gs/unique/index.ts +98 -0
  495. package/package.json +3 -2
@@ -5,6 +5,8 @@ import (
5
5
  "errors"
6
6
  "os"
7
7
  "path/filepath"
8
+ "regexp"
9
+ "slices"
8
10
  "strings"
9
11
  "testing"
10
12
  )
@@ -127,8 +129,8 @@ func TestCompilePackagesEmitsSimplePackage(t *testing.T) {
127
129
  "import * as $ from \"@goscript/builtin/index.js\"",
128
130
  "export const Greeting: string = \"Hello\"",
129
131
  "export function Add(a: number, b: number): number",
130
- "export async function main(): Promise<void>",
131
- "let size = $.len(Greeting)",
132
+ "export async function main(): globalThis.Promise<void>",
133
+ "let size = 5",
132
134
  "$.print(\"total:\", size)",
133
135
  "$.println(Greeting, total)",
134
136
  "$.panic(\"unreachable\")",
@@ -140,118 +142,108 @@ func TestCompilePackagesEmitsSimplePackage(t *testing.T) {
140
142
  }
141
143
  }
142
144
 
143
- func TestCompilePackagesEmitsPackageLocalImport(t *testing.T) {
145
+ func TestCompilePackagesSkipsPureTopLevelBlankAssertions(t *testing.T) {
144
146
  moduleDir := writePackageGraphFixture(t, map[string]string{
145
- "go.mod": "module example.test/imports\n\ngo 1.25.3\n",
147
+ "go.mod": "module example.test/blankassert\n\ngo 1.25.3\n",
146
148
  "main.go": strings.Join([]string{
147
149
  "package main",
148
- "import \"example.test/imports/subpkg\"",
149
- "func main() {",
150
- " var b subpkg.Builder",
151
- " b.Set(\"built\")",
152
- " println(b.Value)",
153
- " println(subpkg.Greet(\"world\"))",
154
- " println(localMessage())",
155
- "}",
156
- "",
157
- }, "\n"),
158
- "helper.go": strings.Join([]string{
159
- "package main",
160
- "func localMessage() string {",
161
- " return \"from helper\"",
162
- "}",
163
- "",
164
- }, "\n"),
165
- "subpkg/subpkg.go": strings.Join([]string{
166
- "package subpkg",
167
- "type Builder struct {",
168
- " Value string",
169
- "}",
170
- "func (b *Builder) Set(value string) {",
171
- " b.Value = value",
172
- "}",
173
- "func Greet(name string) string {",
174
- " return \"Hello, \" + name",
175
- "}",
150
+ "type named interface { Name() string }",
151
+ "type item struct{}",
152
+ "func (*item) Name() string { return \"item\" }",
153
+ "var defaultItem = &item{}",
154
+ "var _ named = defaultItem",
155
+ "func main() { println(defaultItem.Name()) }",
176
156
  "",
177
157
  }, "\n"),
178
158
  })
179
159
  outputDir := filepath.Join(t.TempDir(), "output")
180
- comp, err := NewCompiler(&Config{
181
- Dir: moduleDir,
182
- OutputPath: outputDir,
183
- AllDependencies: true,
184
- }, nil, nil)
160
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
185
161
  if err != nil {
186
162
  t.Fatal(err.Error())
187
163
  }
188
164
 
189
- result, err := comp.CompilePackages(context.Background(), ".")
190
- if err != nil {
165
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
191
166
  t.Fatal(err.Error())
192
167
  }
193
- if result == nil || len(result.CompiledPackages) != 2 {
194
- t.Fatalf("unexpected result: %#v", result)
195
- }
196
- mainFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "main.gs.ts")
197
- mainContent, err := os.ReadFile(mainFile)
168
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "blankassert", "main.gs.ts")
169
+ content, err := os.ReadFile(outputFile)
198
170
  if err != nil {
199
171
  t.Fatal(err.Error())
200
172
  }
201
- if !strings.Contains(string(mainContent), "import * as subpkg from \"@goscript/example.test/imports/subpkg/index.js\"") {
202
- t.Fatalf("missing package-local import:\n%s", string(mainContent))
173
+ text := string(content)
174
+ if strings.Contains(text, "__goscriptBlank") {
175
+ t.Fatalf("pure top-level blank assertion emitted runtime binding:\n%s", text)
203
176
  }
204
- if !strings.Contains(string(mainContent), "let b: $.VarRef<subpkg.Builder> = $.varRef($.markAsStructValue(new subpkg.Builder()))") {
205
- t.Fatalf("missing imported struct zero value qualification:\n%s", string(mainContent))
177
+ if !strings.Contains(text, "export let defaultItem") {
178
+ t.Fatalf("missing real package variable:\n%s", text)
206
179
  }
207
- if !strings.Contains(string(mainContent), "import * as __goscript_helper from \"./helper.gs.ts\"") ||
208
- !strings.Contains(string(mainContent), "$.println(__goscript_helper.localMessage())") {
209
- t.Fatalf("missing same-package helper import:\n%s", string(mainContent))
180
+ }
181
+
182
+ func TestCompilePackagesLazilyInitializesCrossFilePackageVars(t *testing.T) {
183
+ moduleDir := writePackageGraphFixture(t, map[string]string{
184
+ "go.mod": "module example.test/lazyvars\n\ngo 1.25.3\n",
185
+ "a.go": strings.Join([]string{
186
+ "package main",
187
+ "type words []int",
188
+ "type remote struct { n int }",
189
+ "var one = words{1}",
190
+ "func useTwo() int { return two.values[0] + remoteZero.n }",
191
+ "",
192
+ }, "\n"),
193
+ "b.go": strings.Join([]string{
194
+ "package main",
195
+ "type holder struct { values words }",
196
+ "var two = holder{one}",
197
+ "var remoteZero remote",
198
+ "func main() { println(useTwo(), two.values[0]) }",
199
+ "",
200
+ }, "\n"),
201
+ })
202
+ outputDir := filepath.Join(t.TempDir(), "output")
203
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
204
+ if err != nil {
205
+ t.Fatal(err.Error())
210
206
  }
211
- indexFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "subpkg", "index.ts")
212
- indexContent, err := os.ReadFile(indexFile)
207
+
208
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
209
+ t.Fatal(err.Error())
210
+ }
211
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazyvars", "b.gs.ts")
212
+ content, err := os.ReadFile(outputFile)
213
213
  if err != nil {
214
214
  t.Fatal(err.Error())
215
215
  }
216
- if string(indexContent) != "export { Builder, Greet } from \"./subpkg.gs.ts\"\n" {
217
- t.Fatalf("unexpected subpkg index:\n%s", string(indexContent))
216
+ text := string(content)
217
+ for _, want := range []string{
218
+ "export let two: holder = undefined as unknown as holder",
219
+ "export function __goscript_get_two(): holder",
220
+ "export let remoteZero: __goscript_a.remote = undefined as unknown as __goscript_a.remote",
221
+ "export function __goscript_get_remoteZero(): __goscript_a.remote",
222
+ "__goscript_a.one",
223
+ } {
224
+ if !strings.Contains(text, want) {
225
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
226
+ }
218
227
  }
219
- mainIndexFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "index.ts")
220
- mainIndexContent, err := os.ReadFile(mainIndexFile)
228
+ aFile := filepath.Join(outputDir, "@goscript", "example.test", "lazyvars", "a.gs.ts")
229
+ aContent, err := os.ReadFile(aFile)
221
230
  if err != nil {
222
231
  t.Fatal(err.Error())
223
232
  }
224
- if strings.Contains(string(mainIndexContent), "localMessage") {
225
- t.Fatalf("unexported helper leaked into package index:\n%s", string(mainIndexContent))
233
+ if !strings.Contains(string(aContent), "__goscript_b.__goscript_get_two()") {
234
+ t.Fatalf("missing lazy cross-file getter use in a.go output:\n%s", string(aContent))
226
235
  }
227
236
  }
228
237
 
229
- func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
238
+ func TestCompilePackagesLazilyInitializesSameFileLaterPackageVars(t *testing.T) {
230
239
  moduleDir := writePackageGraphFixture(t, map[string]string{
231
- "go.mod": "module example.test/structs\n\ngo 1.25.3\n",
240
+ "go.mod": "module example.test/lazylatervars\n\ngo 1.25.3\n",
232
241
  "main.go": strings.Join([]string{
233
242
  "package main",
234
- "type Counter struct {",
235
- " // Value counts reads.",
236
- " Value int `json:\"value\"`",
237
- "}",
238
- "func (c Counter) Read() int {",
239
- " return c.Value",
240
- "}",
241
- "func (c *Counter) Set(v int) {",
242
- " c.Value = v",
243
- "}",
244
- "func main() {",
245
- " original := Counter{Value: 1}",
246
- "",
247
- " // Copy should stay readable in generated output.",
248
- " copy := original",
249
- " pointer := &original",
250
- " pointer.Set(2)",
251
- " var iface any = pointer",
252
- " _, ok := iface.(*Counter)",
253
- " println(copy.Read(), original.Read(), ok)",
254
- "}",
243
+ "type detail struct { n int }",
244
+ "var table = []detail{{n: later.n}}",
245
+ "var later = detail{n: 7}",
246
+ "func main() { println(table[0].n) }",
255
247
  "",
256
248
  }, "\n"),
257
249
  })
@@ -264,25 +256,16 @@ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
264
256
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
265
257
  t.Fatal(err.Error())
266
258
  }
267
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "structs", "main.gs.ts")
259
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazylatervars", "main.gs.ts")
268
260
  content, err := os.ReadFile(outputFile)
269
261
  if err != nil {
270
262
  t.Fatal(err.Error())
271
263
  }
272
264
  text := string(content)
273
265
  for _, want := range []string{
274
- "export class Counter",
275
- "// Value counts reads.\n\tpublic get Value(): number",
276
- "public clone(): Counter",
277
- "public Read(): number",
278
- "public Set(v: number): void",
279
- "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))",
280
- "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))\n\n\t// Copy should stay readable in generated output.\n\tlet copy",
281
- "let copy = $.markAsStructValue(original.value.clone())",
282
- "let pointer = original",
283
- "$.pointerValue(pointer).Set(2)",
284
- "let [, ok] = $.typeAssertTuple<Counter | $.VarRef<Counter> | null>(iface, { kind: $.TypeKind.Pointer, elemType: \"main.Counter\" })",
285
- "\"Value\": { type: { kind: $.TypeKind.Basic, name: \"int\" }, tag: \"json:\\\"value\\\"\" }",
266
+ "export let table: $.Slice<detail> = undefined as unknown as $.Slice<detail>",
267
+ "export function __goscript_get_table(): $.Slice<detail>",
268
+ "export let later: detail = $.markAsStructValue(new detail({n: 7}))",
286
269
  } {
287
270
  if !strings.Contains(text, want) {
288
271
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -290,19 +273,19 @@ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
290
273
  }
291
274
  }
292
275
 
293
- func TestCompilePackagesEmitsNestedPointerStorageAssertions(t *testing.T) {
276
+ func TestCompilePackagesLazilyInitializesFunctionBodyPackageVarDependencies(t *testing.T) {
294
277
  moduleDir := writePackageGraphFixture(t, map[string]string{
295
- "go.mod": "module example.test/pointers\n\ngo 1.25.3\n",
278
+ "go.mod": "module example.test/lazybodyvars\n\ngo 1.25.3\n",
296
279
  "main.go": strings.Join([]string{
297
280
  "package main",
298
- "func main() {",
299
- " var x int = 10",
300
- " p1 := &x",
301
- " p2 := &p1",
302
- " p3 := &p2",
303
- " ***p3 = 12",
304
- " println(x)",
281
+ "type Point struct { n int }",
282
+ "var first, _ = new(Point).SetBytes()",
283
+ "func (p *Point) SetBytes() (*Point, error) {",
284
+ " p.n = later",
285
+ " return p, nil",
305
286
  "}",
287
+ "var later = 7",
288
+ "func main() { println(first.n) }",
306
289
  "",
307
290
  }, "\n"),
308
291
  })
@@ -315,18 +298,17 @@ func TestCompilePackagesEmitsNestedPointerStorageAssertions(t *testing.T) {
315
298
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
316
299
  t.Fatal(err.Error())
317
300
  }
318
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "pointers", "main.gs.ts")
301
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazybodyvars", "main.gs.ts")
319
302
  content, err := os.ReadFile(outputFile)
320
303
  if err != nil {
321
304
  t.Fatal(err.Error())
322
305
  }
323
306
  text := string(content)
324
307
  for _, want := range []string{
325
- "let x: $.VarRef<number> = $.varRef(10)",
326
- "let p1 = $.varRef(x)",
327
- "let p2 = $.varRef(p1)",
328
- "let p3 = p2",
329
- "$.pointerValue($.pointerValue(p3))!.value = 12",
308
+ "export let first: Point | $.VarRef<Point> | null = undefined as unknown as Point | $.VarRef<Point> | null",
309
+ "export function __goscript_get_first(): Point | $.VarRef<Point> | null",
310
+ "function __goscript_get___goscriptTuple",
311
+ "export let later: number = 7",
330
312
  } {
331
313
  if !strings.Contains(text, want) {
332
314
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -334,31 +316,22 @@ func TestCompilePackagesEmitsNestedPointerStorageAssertions(t *testing.T) {
334
316
  }
335
317
  }
336
318
 
337
- func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
319
+ func TestCompilePackagesInitializesLazyAsyncPackageVarsBeforeInit(t *testing.T) {
338
320
  moduleDir := writePackageGraphFixture(t, map[string]string{
339
- "go.mod": "module example.test/collections\n\ngo 1.25.3\n",
321
+ "go.mod": "module example.test/lazyasyncvars\n\ngo 1.25.3\n",
340
322
  "main.go": strings.Join([]string{
341
323
  "package main",
342
- "type MyInt int",
343
- "func (m MyInt) Double() int { return int(m) * 2 }",
344
- "type MySlice []int",
345
- "func (s *MySlice) Add(v int) { *s = append(*s, v) }",
346
- "func main() {",
347
- " arr := [3]int{1: 10}",
348
- " slice := make([]int, 0, 2)",
349
- " empty := []rune{}",
350
- " literal := []int{1, 2}",
351
- " literal = append(literal, 3)",
352
- " slice = append(slice, 5)",
353
- " slice[0] = arr[1]",
354
- " m := make(map[string]int)",
355
- " m[\"one\"] = 1",
356
- " value, ok := m[\"missing\"]",
357
- " text := \"hé\"",
358
- " var list MySlice",
359
- " list.Add(7)",
360
- " println(arr[1], slice[0], literal[2], len(slice), cap(slice), len(empty), value, ok, text[0], text[1], MyInt(5).Double(), len(list))",
324
+ "import \"sync\"",
325
+ "var lock sync.Mutex",
326
+ "var first = makeFirst()",
327
+ "func makeFirst() int {",
328
+ " lock.Lock()",
329
+ " defer lock.Unlock()",
330
+ " return later",
361
331
  "}",
332
+ "var later = 7",
333
+ "func init() { println(first) }",
334
+ "func main() {}",
362
335
  "",
363
336
  }, "\n"),
364
337
  })
@@ -371,32 +344,18 @@ func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
371
344
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
372
345
  t.Fatal(err.Error())
373
346
  }
374
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "collections", "main.gs.ts")
347
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazyasyncvars", "main.gs.ts")
375
348
  content, err := os.ReadFile(outputFile)
376
349
  if err != nil {
377
350
  t.Fatal(err.Error())
378
351
  }
379
352
  text := string(content)
380
353
  for _, want := range []string{
381
- "export type MyInt = number",
382
- "export function MyInt_Double(m: MyInt): number",
383
- "export type MySlice = $.Slice<number>",
384
- "export function MySlice_Add(s: $.VarRef<MySlice>, v: number): void",
385
- "let arr = [0, 10, 0]",
386
- "let slice = $.makeSlice<number>(0, 2, \"number\")",
387
- "let empty = $.arrayToSlice<number>([])",
388
- "let literal = $.arrayToSlice<number>([1, 2])",
389
- "literal = $.append(literal, 3)",
390
- "slice![0] = arr[1]",
391
- "let m = $.makeMap<string, number>()",
392
- "$.mapSet(m, \"one\", 1)",
393
- "let [value, ok] = $.mapGet(m, \"missing\", 0)",
394
- "slice![0]",
395
- "literal![2]",
396
- "let list: $.VarRef<MySlice> = $.varRef(null)",
397
- "MySlice_Add(list, 7)",
398
- "$.indexStringOrBytes(text, 0)",
399
- "MyInt_Double(5)",
354
+ "async function __goscript_init_first(): globalThis.Promise<void>",
355
+ "first = await makeFirst()",
356
+ "export function __goscript_get_first(): number",
357
+ "await __goscript_init_first()",
358
+ "__goscriptInit0()",
400
359
  } {
401
360
  if !strings.Contains(text, want) {
402
361
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -404,35 +363,17 @@ func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
404
363
  }
405
364
  }
406
365
 
407
- func TestCompilePackagesEmitsInterfacesMethodValuesTypeSwitchesAndFunctionAssertions(t *testing.T) {
366
+ func TestCompilePackagesAssignsLazyPackageVarsDirectly(t *testing.T) {
408
367
  moduleDir := writePackageGraphFixture(t, map[string]string{
409
- "go.mod": "module example.test/interfaces\n\ngo 1.25.3\n",
368
+ "go.mod": "module example.test/lazyassign\n\ngo 1.25.3\n",
410
369
  "main.go": strings.Join([]string{
411
370
  "package main",
412
- "type Greeter func(name string) string",
413
- "func greet(name string) string { return \"hello \" + name }",
414
- "type Reader interface { Read() string }",
415
- "type Closer interface { Close() string }",
416
- "type ReadCloser interface { Reader; Closer }",
417
- "type Counter struct { Value int }",
418
- "func (c *Counter) Inc() { c.Value++ }",
419
- "func (c Counter) Read() string { return \"read\" }",
420
- "func (c Counter) Close() string { return \"close\" }",
421
- "func call(fn func()) { fn() }",
422
- "func main() {",
423
- " counter := &Counter{}",
424
- " call(counter.Inc)",
425
- " var rc ReadCloser = counter",
426
- " _, ok := rc.(ReadCloser)",
427
- " var i any = Greeter(greet)",
428
- " fn, ok := i.(Greeter)",
429
- " var l any = (*struct { Name string })(nil)",
430
- " _, ok2 := l.(*struct { Name string })",
431
- " switch v := rc.(type) {",
432
- " case ReadCloser:",
433
- " println(v.Read(), fn(\"gopher\"), ok, ok2)",
434
- " }",
371
+ "var table = []int{later}",
372
+ "var later = 1",
373
+ "func init() {",
374
+ " table = append(table, 2)",
435
375
  "}",
376
+ "func main() { println(len(table)) }",
436
377
  "",
437
378
  }, "\n"),
438
379
  })
@@ -445,46 +386,35 @@ func TestCompilePackagesEmitsInterfacesMethodValuesTypeSwitchesAndFunctionAssert
445
386
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
446
387
  t.Fatal(err.Error())
447
388
  }
448
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "interfaces", "main.gs.ts")
389
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazyassign", "main.gs.ts")
449
390
  content, err := os.ReadFile(outputFile)
450
391
  if err != nil {
451
392
  t.Fatal(err.Error())
452
393
  }
453
394
  text := string(content)
454
- for _, want := range []string{
455
- "export type Greeter = ((name: string) => string) | null",
456
- "export type ReadCloser = null | {",
457
- "Read(): string",
458
- "Close(): string",
459
- "$.registerInterfaceType(\n\t\"main.ReadCloser\"",
460
- "((__receiver) => () => __receiver.Inc())($.pointerValue(counter))",
461
- "$.namedFunction(greet, \"main.Greeter\")",
462
- "$.typedNil(\"*struct{Name string}\")",
463
- "elemType: { kind: $.TypeKind.Struct, methods: [], fields: {\"Name\": { kind: $.TypeKind.Basic, name: \"string\" }} }",
464
- "let fn = __goscriptTuple",
465
- "$.typeSwitch(",
466
- "types: [\"main.ReadCloser\"]",
467
- } {
468
- if !strings.Contains(text, want) {
469
- t.Fatalf("missing %q in generated output:\n%s", want, text)
470
- }
395
+ if !strings.Contains(text, "table = $.append(__goscript_get_table(), 2)") {
396
+ t.Fatalf("missing direct lazy package var assignment:\n%s", text)
397
+ }
398
+ if strings.Contains(text, "__goscript_get_table() =") {
399
+ t.Fatalf("lazy getter used as assignment target:\n%s", text)
471
400
  }
472
401
  }
473
402
 
474
- func TestCompilePackagesAssertsInterfaceMethodReceivers(t *testing.T) {
403
+ func TestCompilePackagesAssignsImportedPackageVarsThroughSetters(t *testing.T) {
475
404
  moduleDir := writePackageGraphFixture(t, map[string]string{
476
- "go.mod": "module example.test/interface-receivers\n\ngo 1.25.3\n",
405
+ "go.mod": "module example.test/pkgvarassign\n\ngo 1.25.3\n",
406
+ "dep/dep.go": strings.Join([]string{
407
+ "package dep",
408
+ "var Count int",
409
+ "",
410
+ }, "\n"),
477
411
  "main.go": strings.Join([]string{
478
412
  "package main",
479
- "type FileInfo interface { Name() string }",
480
- "type fileInfo struct { name string }",
481
- "func (f fileInfo) Name() string { return f.name }",
482
- "func stat() (FileInfo, error) { return fileInfo{name: \"demo\"}, nil }",
483
- "func main() {",
484
- " info, err := stat()",
485
- " if err == nil {",
486
- " println(info.Name())",
487
- " }",
413
+ "import \"example.test/pkgvarassign/dep\"",
414
+ "func bump() {",
415
+ " dep.Count = 1",
416
+ " dep.Count += 16",
417
+ " dep.Count++",
488
418
  "}",
489
419
  "",
490
420
  }, "\n"),
@@ -498,43 +428,37 @@ func TestCompilePackagesAssertsInterfaceMethodReceivers(t *testing.T) {
498
428
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
499
429
  t.Fatal(err.Error())
500
430
  }
501
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "interface-receivers", "main.gs.ts")
431
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "pkgvarassign", "main.gs.ts")
502
432
  content, err := os.ReadFile(outputFile)
503
433
  if err != nil {
504
434
  t.Fatal(err.Error())
505
435
  }
506
436
  text := string(content)
507
437
  for _, want := range []string{
508
- "export type FileInfo = null | {",
509
- "$.println(info!.Name())",
438
+ "dep.__goscript_set_Count(1)",
439
+ "dep.__goscript_set_Count(dep.Count + 16)",
440
+ "dep.__goscript_set_Count(dep.Count + 1)",
510
441
  } {
511
442
  if !strings.Contains(text, want) {
512
- t.Fatalf("missing %q in generated output:\n%s", want, text)
443
+ t.Fatalf("missing imported package var setter assignment %q:\n%s", want, text)
513
444
  }
514
445
  }
446
+ if strings.Contains(text, "dep.Count +=") || strings.Contains(text, "dep.Count++") {
447
+ t.Fatalf("imported package var assigned directly:\n%s", text)
448
+ }
515
449
  }
516
450
 
517
- func TestCompilePackagesBoxesTypedNilInterfaceValues(t *testing.T) {
451
+ func TestCompilePackagesAliasesForInitShortDeclShadow(t *testing.T) {
518
452
  moduleDir := writePackageGraphFixture(t, map[string]string{
519
- "go.mod": "module example.test/typed-nil-interface\n\ngo 1.25.3\n",
453
+ "go.mod": "module example.test/forinitshadow\n\ngo 1.25.3\n",
520
454
  "main.go": strings.Join([]string{
521
455
  "package main",
522
- "type Animal interface { Name() string }",
523
- "type Dog struct { name string }",
524
- "func (d *Dog) Name() string {",
525
- " if d == nil {",
526
- " return \"unknown dog\"",
456
+ "func checksum(n int) int {",
457
+ " total := 0",
458
+ " for n := n - 4; total <= n; total++ {",
459
+ " total += n",
527
460
  " }",
528
- " return d.name",
529
- "}",
530
- "func FindDog() *Dog { return nil }",
531
- "func FindAnimal() Animal { return Animal(FindDog()) }",
532
- "func main() {",
533
- " animal := FindAnimal()",
534
- " println(animal.Name())",
535
- " var dog *Dog = nil",
536
- " var a Animal = dog",
537
- " println(a == nil)",
461
+ " return total + n",
538
462
  "}",
539
463
  "",
540
464
  }, "\n"),
@@ -548,54 +472,38 @@ func TestCompilePackagesBoxesTypedNilInterfaceValues(t *testing.T) {
548
472
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
549
473
  t.Fatal(err.Error())
550
474
  }
551
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "typed-nil-interface", "main.gs.ts")
475
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "forinitshadow", "main.gs.ts")
552
476
  content, err := os.ReadFile(outputFile)
553
477
  if err != nil {
554
478
  t.Fatal(err.Error())
555
479
  }
556
480
  text := string(content)
557
- for _, want := range []string{
558
- "return $.interfaceValue<Animal>(FindDog(), \"*main.Dog\")",
559
- "$.println(animal!.Name())",
560
- "let a: Animal = $.interfaceValue<Animal>(dog, \"*main.Dog\")",
561
- } {
562
- if !strings.Contains(text, want) {
563
- t.Fatalf("missing %q in generated output:\n%s", want, text)
564
- }
481
+ if strings.Contains(text, "for (let n = n - 4") {
482
+ t.Fatalf("for init short declaration references itself:\n%s", text)
483
+ }
484
+ if !regexp.MustCompile(`let __goscriptShadow\d+ = n\s+for \(let __goscriptShadow\d+ = __goscriptShadow\d+ - 4; total <= __goscriptShadow\d+; total\+\+\)`).MatchString(text) {
485
+ t.Fatalf("missing aliased for init shadow declaration:\n%s", text)
486
+ }
487
+ if !strings.Contains(text, "return total + n") {
488
+ t.Fatalf("outer n was not preserved after the loop:\n%s", text)
565
489
  }
566
490
  }
567
491
 
568
- func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T) {
492
+ func TestCompilePackagesReadsShadowedVarRefStructFieldsOnce(t *testing.T) {
569
493
  moduleDir := writePackageGraphFixture(t, map[string]string{
570
- "go.mod": "module example.test/generics\n\ngo 1.25.3\n",
494
+ "go.mod": "module example.test/shadowvarreffield\n\ngo 1.25.3\n",
571
495
  "main.go": strings.Join([]string{
572
496
  "package main",
573
- "type Stringer interface { String() string }",
574
- "type MyInt int",
575
- "func (m MyInt) String() string { return \"int\" }",
576
- "type Box[T any] struct { value T }",
577
- "func (b Box[T]) Get() T { return b.value }",
578
- "func NewBox[T any](value T) Box[T] { return Box[T]{value: value} }",
579
- "type Set[T comparable] map[T]struct{}",
580
- "func ZeroValue[T Stringer]() T {",
581
- " var zero T",
582
- " return zero",
583
- "}",
584
- "func CallString[T Stringer](v T) string { return v.String() }",
585
- "func Sum[T Stringer](vals ...T) T {",
586
- " var zero T",
587
- " return zero",
588
- "}",
589
- "func main() {",
590
- " box := NewBox(7)",
591
- " println(box.Get())",
592
- " seen := make(Set[int])",
593
- " seen[1] = struct{}{}",
594
- " zero := ZeroValue[MyInt]()",
595
- " println(CallString(zero))",
596
- " sum := Sum[MyInt]()",
597
- " println(CallString(sum))",
497
+ "type key struct { pad []byte }",
498
+ "func fill(k *key) error { return nil }",
499
+ "func size() int {",
500
+ " var key key",
501
+ " if err := fill(&key); err != nil {",
502
+ " return 0",
503
+ " }",
504
+ " return len(key.pad)",
598
505
  "}",
506
+ "func main() { println(size()) }",
599
507
  "",
600
508
  }, "\n"),
601
509
  })
@@ -608,47 +516,33 @@ func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T)
608
516
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
609
517
  t.Fatal(err.Error())
610
518
  }
611
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "generics", "main.gs.ts")
519
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "shadowvarreffield", "main.gs.ts")
612
520
  content, err := os.ReadFile(outputFile)
613
521
  if err != nil {
614
522
  t.Fatal(err.Error())
615
523
  }
616
524
  text := string(content)
617
- for _, want := range []string{
618
- "public Get(): any",
619
- "export function NewBox(__typeArgs: $.GenericTypeArgs | undefined, value: any): Box",
620
- "let seen = $.makeMap<number, Record<string, unknown>>()",
621
- "$.mapSet(seen, 1, {})",
622
- "$.genericZero(__typeArgs, \"T\", null)",
623
- "$.callGenericMethod(__typeArgs, \"T\", \"String\", v)",
624
- "ZeroValue({T: { type: \"main.MyInt\", zero: () => 0, methods: {String: MyInt_String} }})",
625
- "CallString({T: { type: \"main.MyInt\", zero: () => 0, methods: {String: MyInt_String} }}, zero)",
626
- "Sum({T: { type: \"main.MyInt\", zero: () => 0, methods: {String: MyInt_String} }}, null)",
627
- } {
628
- if !strings.Contains(text, want) {
629
- t.Fatalf("missing %q in generated output:\n%s", want, text)
630
- }
525
+ if !strings.Contains(text, "$.len(__goscriptShadow0.value.pad)") {
526
+ t.Fatalf("missing single dereference field read:\n%s", text)
527
+ }
528
+ if strings.Contains(text, ".value.value.pad") {
529
+ t.Fatalf("shadowed VarRef struct field was dereferenced twice:\n%s", text)
631
530
  }
632
531
  }
633
532
 
634
- func TestCompilePackagesAttachesFunctionLiteralTypeInfo(t *testing.T) {
533
+ func TestCompilePackagesWrapsChannelSendInterfaceValues(t *testing.T) {
635
534
  moduleDir := writePackageGraphFixture(t, map[string]string{
636
- "go.mod": "module example.test/function-type-info\n\ngo 1.25.3\n",
535
+ "go.mod": "module example.test/chansendiface\n\ngo 1.25.3\n",
637
536
  "main.go": strings.Join([]string{
638
537
  "package main",
639
- "type Callback func(value int) string",
640
- "func call(cb Callback) string {",
641
- " return cb(1)",
642
- "}",
643
- "func main() {",
644
- " fn := func(value int) string {",
645
- " return \"\"",
646
- " }",
647
- " var cb Callback = nil",
648
- " _ = fn",
649
- " _ = cb",
650
- " _ = call(fn)",
538
+ "type item interface { Name() string }",
539
+ "type concrete struct{}",
540
+ "func (*concrete) Name() string { return \"ok\" }",
541
+ "func send(ch chan item) {",
542
+ " v := &concrete{}",
543
+ " ch <- v",
651
544
  "}",
545
+ "func main() {}",
652
546
  "",
653
547
  }, "\n"),
654
548
  })
@@ -661,59 +555,30 @@ func TestCompilePackagesAttachesFunctionLiteralTypeInfo(t *testing.T) {
661
555
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
662
556
  t.Fatal(err.Error())
663
557
  }
664
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "function-type-info", "main.gs.ts")
558
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "chansendiface", "main.gs.ts")
665
559
  content, err := os.ReadFile(outputFile)
666
560
  if err != nil {
667
561
  t.Fatal(err.Error())
668
562
  }
669
563
  text := string(content)
670
- for _, want := range []string{
671
- "export type Callback = ((value: number) => string) | null",
672
- "export function call(cb: Callback): string {\n\treturn cb!(1)",
673
- "$.functionValue((value: number): string => {",
674
- "kind: $.TypeKind.Function",
675
- "params: [{ kind: $.TypeKind.Basic, name: \"int\" }]",
676
- "results: [{ kind: $.TypeKind.Basic, name: \"string\" }]",
677
- } {
678
- if !strings.Contains(text, want) {
679
- t.Fatalf("missing %q in generated output:\n%s", want, text)
680
- }
564
+ if !strings.Contains(text, "$.chanSend(ch, $.interfaceValue<item | null>(v, \"*main.concrete\"))") {
565
+ t.Fatalf("missing interface wrapper for channel send:\n%s", text)
681
566
  }
682
567
  }
683
568
 
684
- func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
569
+ func TestCompilePackagesBindsFuncLiteralVarRefParams(t *testing.T) {
685
570
  moduleDir := writePackageGraphFixture(t, map[string]string{
686
- "go.mod": "module example.test/variadic\n\ngo 1.25.3\n",
571
+ "go.mod": "module example.test/funclitvarref\n\ngo 1.25.3\n",
687
572
  "main.go": strings.Join([]string{
688
573
  "package main",
689
- "type Collector func(label string, parts ...string) string",
690
- "type Joiner interface {",
691
- " Join(parts ...string) string",
692
- "}",
693
- "type Path struct{}",
694
- "func collect(label string, parts ...string) string {",
695
- " for _, part := range parts {",
696
- " if part == \"\" {",
697
- " return label",
698
- " }",
699
- " }",
700
- " return label + string(rune(len(parts)+'0'))",
701
- "}",
702
- "func maybeErr(parts ...string) error { return nil }",
703
- "func (Path) Join(parts ...string) string {",
704
- " return collect(\"method\", parts...)",
705
- "}",
574
+ "type words []int",
575
+ "func (w *words) Add(v int) { *w = append(*w, v) }",
706
576
  "func main() {",
707
- " parts := []string{\"a\", \"b\"}",
708
- " collect(\"none\")",
709
- " collect(\"two\", \"a\", \"b\")",
710
- " collect(\"spread\", parts...)",
711
- " parts = append(parts, \"c\", \"d\")",
712
- " maybeErr(\"ok\")",
713
- " var fn Collector = collect",
714
- " fn(\"fn\", \"x\")",
715
- " var joiner Joiner = Path{}",
716
- " joiner.Join(\"q\", \"r\")",
577
+ " addLen := func(w words) int {",
578
+ " w.Add(7)",
579
+ " return len(w)",
580
+ " }",
581
+ " println(addLen(nil))",
717
582
  "}",
718
583
  "",
719
584
  }, "\n"),
@@ -727,26 +592,16 @@ func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
727
592
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
728
593
  t.Fatal(err.Error())
729
594
  }
730
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "variadic", "main.gs.ts")
595
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "funclitvarref", "main.gs.ts")
731
596
  content, err := os.ReadFile(outputFile)
732
597
  if err != nil {
733
598
  t.Fatal(err.Error())
734
599
  }
735
600
  text := string(content)
736
601
  for _, want := range []string{
737
- "export type Collector = ((label: string, parts: $.Slice<string>) => string) | null",
738
- "Join(parts: $.Slice<string>): string",
739
- "export function collect(label: string, parts: $.Slice<string>): string",
740
- "let part = parts![__rangeIndex]",
741
- "export function maybeErr(parts: $.Slice<string>): $.GoError",
742
- "public Join(parts: $.Slice<string>): string",
743
- "collect(\"none\", null)",
744
- "collect(\"two\", $.arrayToSlice<string>([\"a\", \"b\"]))",
745
- "collect(\"spread\", parts)",
746
- "$.append(parts, \"c\", \"d\")",
747
- "maybeErr($.arrayToSlice<string>([\"ok\"]))",
748
- "fn!(\"fn\", $.arrayToSlice<string>([\"x\"]))",
749
- "joiner!.Join($.arrayToSlice<string>([\"q\", \"r\"]))",
602
+ "(__goscriptParam0: words): number => {",
603
+ "let w: $.VarRef<words> = $.varRef(__goscriptParam0)",
604
+ "words_Add(w, 7)",
750
605
  } {
751
606
  if !strings.Contains(text, want) {
752
607
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -754,33 +609,19 @@ func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
754
609
  }
755
610
  }
756
611
 
757
- func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
612
+ func TestCompilePackagesAnnotatesNewPointerShortDecls(t *testing.T) {
758
613
  moduleDir := writePackageGraphFixture(t, map[string]string{
759
- "go.mod": "module example.test/iterators\n\ngo 1.25.3\n",
614
+ "go.mod": "module example.test/newptrdecl\n\ngo 1.25.3\n",
760
615
  "main.go": strings.Join([]string{
761
616
  "package main",
762
- "func pairs(yield func(int, string) bool) {",
763
- " values := []string{\"a\", \"b\"}",
764
- " for i, v := range values {",
765
- " if !yield(i, v) {",
766
- " break",
767
- " }",
768
- " }",
769
- "}",
617
+ "type OID []int",
618
+ "func use(*OID) {}",
770
619
  "func main() {",
771
- " for i, v := range pairs {",
772
- " println(i, v)",
773
- " }",
774
- " var last int",
775
- " for last = range pairs {",
776
- " println(last)",
777
- " }",
778
- " for i := range 3 {",
779
- " if i == 1 {",
780
- " continue",
781
- " }",
782
- " println(i)",
620
+ " oid := new(OID)",
621
+ " if len(*oid) == 0 {",
622
+ " oid = nil",
783
623
  " }",
624
+ " use(oid)",
784
625
  "}",
785
626
  "",
786
627
  }, "\n"),
@@ -794,19 +635,16 @@ func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
794
635
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
795
636
  t.Fatal(err.Error())
796
637
  }
797
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "iterators", "main.gs.ts")
638
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "newptrdecl", "main.gs.ts")
798
639
  content, err := os.ReadFile(outputFile)
799
640
  if err != nil {
800
641
  t.Fatal(err.Error())
801
642
  }
802
643
  text := string(content)
803
644
  for _, want := range []string{
804
- "if (!_yield!(i, v))",
805
- "break",
806
- "pairs!((i, v) => {",
807
- "last = __goscriptRange",
808
- "return true",
809
- "continue",
645
+ "let oid: $.VarRef<OID> | null = $.varRef<OID>(null as OID)",
646
+ "oid = null",
647
+ "use(oid)",
810
648
  } {
811
649
  if !strings.Contains(text, want) {
812
650
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -814,29 +652,16 @@ func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
814
652
  }
815
653
  }
816
654
 
817
- func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
655
+ func TestCompilePackagesEmitsShadowedBuiltinCalls(t *testing.T) {
818
656
  moduleDir := writePackageGraphFixture(t, map[string]string{
819
- "go.mod": "module example.test/async\n\ngo 1.25.3\n",
657
+ "go.mod": "module example.test/shadowbuiltin\n\ngo 1.25.3\n",
820
658
  "main.go": strings.Join([]string{
821
- "package main",
822
- "type Processor interface { Process(v int) int }",
823
- "type Worker struct { ch chan int }",
824
- "func (w *Worker) Process(v int) int {",
825
- " w.ch <- v",
826
- " return <-w.ch",
659
+ "package shadowbuiltin",
660
+ "type Value struct {",
661
+ " N int",
827
662
  "}",
828
- "func call(p Processor) int { return p.Process(2) }",
829
- "func main() {",
830
- " ch := make(chan int, 1)",
831
- " defer func() { <-ch }()",
832
- " go func() { ch <- 1 }()",
833
- " select {",
834
- " case v := <-ch:",
835
- " println(v)",
836
- " default:",
837
- " println(\"default\")",
838
- " }",
839
- " _ = call(&Worker{ch: make(chan int, 1)})",
663
+ "func Build(new func() (*Value, error)) (*Value, error) {",
664
+ " return new()",
840
665
  "}",
841
666
  "",
842
667
  }, "\n"),
@@ -850,45 +675,24 @@ func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
850
675
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
851
676
  t.Fatal(err.Error())
852
677
  }
853
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "async", "main.gs.ts")
678
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "shadowbuiltin", "main.gs.ts")
854
679
  content, err := os.ReadFile(outputFile)
855
680
  if err != nil {
856
681
  t.Fatal(err.Error())
857
682
  }
858
683
  text := string(content)
859
- for _, want := range []string{
860
- "Process(v: number): Promise<number>",
861
- "public async Process(v: number): Promise<number>",
862
- "let ch = $.makeChannel<number>(1, 0, \"both\")",
863
- "await $.chanSend($.pointerValue(w).ch, v)",
864
- "return await $.chanRecv($.pointerValue(w).ch)",
865
- "await using __defer = new $.AsyncDisposableStack()",
866
- "queueMicrotask(async () => { await ($.functionValue(async (): Promise<void> => {",
867
- "$.selectStatement<any, void>([",
868
- "let v = result.value",
869
- "await call(new Worker({ch: $.makeChannel<number>(1, 0, \"both\")}))",
870
- } {
871
- if !strings.Contains(text, want) {
872
- t.Fatalf("missing %q in generated output:\n%s", want, text)
873
- }
684
+ if !strings.Contains(text, "return await _new!()") {
685
+ t.Fatalf("shadowed builtin call was not emitted as a callable value:\n%s", text)
874
686
  }
875
687
  }
876
688
 
877
- func TestCompilePackagesScopesIfInitDeclarations(t *testing.T) {
689
+ func TestCompilePackagesQuotesRawStringLiterals(t *testing.T) {
878
690
  moduleDir := writePackageGraphFixture(t, map[string]string{
879
- "go.mod": "module example.test/ifinit\n\ngo 1.25.3\n",
691
+ "go.mod": "module example.test/rawstrings\n\ngo 1.25.3\n",
880
692
  "main.go": strings.Join([]string{
881
- "package main",
882
- "func pair() (string, bool) {",
883
- " return \"value\", true",
884
- "}",
885
- "func main() {",
886
- " if value, ok := pair(); ok {",
887
- " println(value)",
888
- " }",
889
- " if value, ok := pair(); ok {",
890
- " println(value)",
891
- " }",
693
+ "package rawstrings",
694
+ "func Values() (string, string) {",
695
+ " return `\\u00`, `invalid escape char after \\`",
892
696
  "}",
893
697
  "",
894
698
  }, "\n"),
@@ -902,43 +706,25 @@ func TestCompilePackagesScopesIfInitDeclarations(t *testing.T) {
902
706
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
903
707
  t.Fatal(err.Error())
904
708
  }
905
- content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "ifinit", "main.gs.ts"))
709
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "rawstrings", "main.gs.ts")
710
+ content, err := os.ReadFile(outputFile)
906
711
  if err != nil {
907
712
  t.Fatal(err.Error())
908
713
  }
909
- if strings.Count(string(content), "{\n\t\tlet [value, ok] = pair()") != 2 {
910
- t.Fatalf("if init declarations were not block scoped:\n%s", string(content))
714
+ text := string(content)
715
+ if !strings.Contains(text, `return ["\\u00", "invalid escape char after \\"]`) {
716
+ t.Fatalf("raw string literals were not quoted for TypeScript:\n%s", text)
911
717
  }
912
718
  }
913
719
 
914
- func TestCompilePackagesLowersSwitchesAndFunctionValueCalls(t *testing.T) {
720
+ func TestCompilePackagesEmitsBinaryStringLiterals(t *testing.T) {
915
721
  moduleDir := writePackageGraphFixture(t, map[string]string{
916
- "go.mod": "module example.test/switchcall\n\ngo 1.25.3\n",
722
+ "go.mod": "module example.test/binarystrings\n\ngo 1.25.3\n",
917
723
  "main.go": strings.Join([]string{
918
- "package main",
919
- "func main() {",
920
- " value := 2",
921
- " switch value {",
922
- " case 1:",
923
- " println(\"one\")",
924
- " case 2, 3:",
925
- " local := \"two-three\"",
926
- " println(local)",
927
- " default:",
928
- " println(\"other\")",
929
- " }",
930
- " switch {",
931
- " case value > 1:",
932
- " println(\"positive\")",
933
- " }",
934
- " release := func() { println(\"release\") }",
935
- " rel := &release",
936
- " (*rel)()",
937
- " wrapped := func() {",
938
- " defer println(\"wrapped deferred\")",
939
- " println(\"wrapped body\")",
940
- " }",
941
- " wrapped()",
724
+ "package binarystrings",
725
+ "const DecodeMap = \"\\xff\\x80A\"",
726
+ "func Value() string {",
727
+ " return DecodeMap",
942
728
  "}",
943
729
  "",
944
730
  }, "\n"),
@@ -952,86 +738,2152 @@ func TestCompilePackagesLowersSwitchesAndFunctionValueCalls(t *testing.T) {
952
738
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
953
739
  t.Fatal(err.Error())
954
740
  }
955
- content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "switchcall", "main.gs.ts"))
741
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "binarystrings", "main.gs.ts")
742
+ content, err := os.ReadFile(outputFile)
956
743
  if err != nil {
957
744
  t.Fatal(err.Error())
958
745
  }
959
- text := string(content)
960
- for _, want := range []string{
961
- "switch (value) {",
962
- "case 2:",
963
- "case 3:",
964
- "let local = \"two-three\"",
965
- "switch (true) {",
966
- "($.pointerValue(rel))!()",
967
- "$.functionValue((): void => {\n\t\tusing __defer = new $.DisposableStack()",
968
- "__defer.defer(() => { $.println(\"wrapped deferred\") })",
969
- "$.println(\"wrapped body\")",
970
- } {
971
- if !strings.Contains(text, want) {
972
- t.Fatalf("missing %q in generated output:\n%s", want, text)
973
- }
974
- }
975
- if strings.Count(text, "\t\tbreak\n") < 3 {
976
- t.Fatalf("switch cases were not rendered with implicit breaks:\n%s", text)
746
+ if !strings.Contains(string(content), "$.bytesToString(new Uint8Array([255, 128, 65]))") {
747
+ t.Fatalf("binary string literal was not emitted as byte-backed string:\n%s", string(content))
977
748
  }
978
749
  }
979
750
 
980
- func TestCompilePackagesLowersMethodValuesWithFixedParameters(t *testing.T) {
751
+ func TestCompilePackagesUsesEmbedOverride(t *testing.T) {
981
752
  moduleDir := writePackageGraphFixture(t, map[string]string{
982
- "go.mod": "module example.test/methodvalue\n\ngo 1.25.3\n",
753
+ "go.mod": "module example.test/embedblank\n\ngo 1.25.3\n",
754
+ "version.txt": "1.2.3\n",
755
+ "version..txt": "4.5.6\n",
756
+ "binary.bin": string([]byte{0x00, 0xff, 0x80, 0x41}),
983
757
  "main.go": strings.Join([]string{
984
- "package main",
985
- "type Counter int",
986
- "func (c Counter) Add(n int) int {",
987
- " return int(c) + n",
988
- "}",
989
- "type Runner struct{}",
990
- "func (r Runner) Run() {",
991
- " println(\"run\")",
992
- "}",
993
- "func main() {",
994
- " c := Counter(4)",
995
- " add := c.Add",
996
- " println(add(3))",
997
- " r := Runner{}",
998
- " run := r.Run",
999
- " run()",
758
+ "package embedblank",
759
+ "import _ \"embed\"",
760
+ "//go:embed version.txt",
761
+ "var Version string",
762
+ "//go:embed version..txt",
763
+ "var Dotted string",
764
+ "//go:embed binary.bin",
765
+ "var Binary []byte",
766
+ "func GetVersion() string {",
767
+ " return Version",
1000
768
  "}",
1001
769
  "",
1002
770
  }, "\n"),
1003
771
  })
1004
772
  outputDir := filepath.Join(t.TempDir(), "output")
1005
- comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
773
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir, AllDependencies: true}, nil, nil)
1006
774
  if err != nil {
1007
775
  t.Fatal(err.Error())
1008
776
  }
1009
777
 
1010
- if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
778
+ result, err := comp.CompilePackages(context.Background(), ".")
779
+ if err != nil {
1011
780
  t.Fatal(err.Error())
1012
781
  }
1013
- content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "methodvalue", "main.gs.ts"))
782
+ if !slices.Contains(result.CopiedPackages, "embed") {
783
+ t.Fatalf("embed override was not copied: %#v", result.CopiedPackages)
784
+ }
785
+ if _, err := os.Stat(filepath.Join(outputDir, "@goscript", "embed", "index.ts")); err != nil {
786
+ t.Fatalf("embed override missing from output: %v", err)
787
+ }
788
+ if _, err := os.Stat(filepath.Join(outputDir, "@goscript", "embed", "embed.gs.ts")); !os.IsNotExist(err) {
789
+ t.Fatalf("stdlib embed was emitted instead of override: %v", err)
790
+ }
791
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "embedblank", "main.gs.ts"))
1014
792
  if err != nil {
1015
793
  t.Fatal(err.Error())
1016
794
  }
1017
- text := string(content)
1018
- for _, want := range []string{
1019
- "((__receiver) => (n: number) => Counter_Add(__receiver, n))(c)",
1020
- "((__receiver) => () => __receiver.Run())(",
1021
- } {
1022
- if !strings.Contains(text, want) {
1023
- t.Fatalf("missing %q in generated output:\n%s", want, text)
1024
- }
795
+ if !strings.Contains(string(content), "export let Version: string = \"1.2.3\\n\"") {
796
+ t.Fatalf("embedded string content was not emitted:\n%s", string(content))
1025
797
  }
1026
- if strings.Contains(text, "...args: any[]") {
1027
- t.Fatalf("method value lowering still uses spread args:\n%s", text)
798
+ if !strings.Contains(string(content), "export let Dotted: string = \"4.5.6\\n\"") {
799
+ t.Fatalf("embedded dotted filename content was not emitted:\n%s", string(content))
800
+ }
801
+ if !strings.Contains(string(content), "export let Binary: $.Slice<number> = new Uint8Array([0, 255, 128, 65])") {
802
+ t.Fatalf("embedded binary content was not emitted as byte values:\n%s", string(content))
1028
803
  }
1029
804
  }
1030
805
 
1031
- func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testing.T) {
806
+ func TestCompilePackagesEmitsPackageLocalImport(t *testing.T) {
1032
807
  moduleDir := writePackageGraphFixture(t, map[string]string{
1033
- "go.mod": "module example.test/qualified\n\ngo 1.25.3\n",
1034
- "lib/lib.go": strings.Join([]string{
808
+ "go.mod": "module example.test/imports\n\ngo 1.25.3\n",
809
+ "main.go": strings.Join([]string{
810
+ "package main",
811
+ "import \"example.test/imports/subpkg\"",
812
+ "func main() {",
813
+ " var b subpkg.Builder",
814
+ " b.Set(\"built\")",
815
+ " println(b.Value)",
816
+ " println(subpkg.Greet(\"world\"))",
817
+ " println(localMessage())",
818
+ "}",
819
+ "",
820
+ }, "\n"),
821
+ "helper.go": strings.Join([]string{
822
+ "package main",
823
+ "func localMessage() string {",
824
+ " return \"from helper\"",
825
+ "}",
826
+ "",
827
+ }, "\n"),
828
+ "subpkg/subpkg.go": strings.Join([]string{
829
+ "package subpkg",
830
+ "type Builder struct {",
831
+ " Value string",
832
+ "}",
833
+ "func (b *Builder) Set(value string) {",
834
+ " b.Value = value",
835
+ "}",
836
+ "func Greet(name string) string {",
837
+ " return \"Hello, \" + name",
838
+ "}",
839
+ "",
840
+ }, "\n"),
841
+ })
842
+ outputDir := filepath.Join(t.TempDir(), "output")
843
+ comp, err := NewCompiler(&Config{
844
+ Dir: moduleDir,
845
+ OutputPath: outputDir,
846
+ AllDependencies: true,
847
+ }, nil, nil)
848
+ if err != nil {
849
+ t.Fatal(err.Error())
850
+ }
851
+
852
+ result, err := comp.CompilePackages(context.Background(), ".")
853
+ if err != nil {
854
+ t.Fatal(err.Error())
855
+ }
856
+ if result == nil || len(result.CompiledPackages) != 2 {
857
+ t.Fatalf("unexpected result: %#v", result)
858
+ }
859
+ mainFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "main.gs.ts")
860
+ mainContent, err := os.ReadFile(mainFile)
861
+ if err != nil {
862
+ t.Fatal(err.Error())
863
+ }
864
+ if !strings.Contains(string(mainContent), "import * as subpkg from \"@goscript/example.test/imports/subpkg/index.js\"") {
865
+ t.Fatalf("missing package-local import:\n%s", string(mainContent))
866
+ }
867
+ if !strings.Contains(string(mainContent), "let b: $.VarRef<subpkg.Builder> = $.varRef($.markAsStructValue(new subpkg.Builder()))") {
868
+ t.Fatalf("missing imported struct zero value qualification:\n%s", string(mainContent))
869
+ }
870
+ if !strings.Contains(string(mainContent), "import * as __goscript_helper from \"./helper.gs.ts\"") ||
871
+ !strings.Contains(string(mainContent), "$.println(__goscript_helper.localMessage())") {
872
+ t.Fatalf("missing same-package helper import:\n%s", string(mainContent))
873
+ }
874
+ indexFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "subpkg", "index.ts")
875
+ indexContent, err := os.ReadFile(indexFile)
876
+ if err != nil {
877
+ t.Fatal(err.Error())
878
+ }
879
+ if string(indexContent) != "export { Builder, Greet } from \"./subpkg.gs.ts\"\n" {
880
+ t.Fatalf("unexpected subpkg index:\n%s", string(indexContent))
881
+ }
882
+ mainIndexFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "index.ts")
883
+ mainIndexContent, err := os.ReadFile(mainIndexFile)
884
+ if err != nil {
885
+ t.Fatal(err.Error())
886
+ }
887
+ if strings.Contains(string(mainIndexContent), "localMessage") {
888
+ t.Fatalf("unexported helper leaked into package index:\n%s", string(mainIndexContent))
889
+ }
890
+ }
891
+
892
+ func TestCompilePackagesPreservesSourceImportAliasesForAssociatedMethods(t *testing.T) {
893
+ moduleDir := writePackageGraphFixture(t, map[string]string{
894
+ "go.mod": "module example.test/sourcealiases\n\ngo 1.25.3\n",
895
+ "dep/block/errors.go": strings.Join([]string{
896
+ "package block",
897
+ "import \"errors\"",
898
+ "var ErrUnexpectedType = errors.New(\"unexpected object type\")",
899
+ "",
900
+ }, "\n"),
901
+ "dep/kvtx/block/store.go": strings.Join([]string{
902
+ "package kvtx_block",
903
+ "type KeyValueStore struct{}",
904
+ "",
905
+ }, "\n"),
906
+ "world/world.go": strings.Join([]string{
907
+ "package world",
908
+ "import block \"example.test/sourcealiases/dep/kvtx/block\"",
909
+ "type World struct {",
910
+ " Object *block.KeyValueStore",
911
+ "}",
912
+ "",
913
+ }, "\n"),
914
+ "world/block-world.go": strings.Join([]string{
915
+ "package world",
916
+ "import (",
917
+ " block \"example.test/sourcealiases/dep/block\"",
918
+ " block_kvtx \"example.test/sourcealiases/dep/kvtx/block\"",
919
+ ")",
920
+ "func (w *World) Apply(next any) error {",
921
+ " if _, ok := next.(*block_kvtx.KeyValueStore); !ok {",
922
+ " return block.ErrUnexpectedType",
923
+ " }",
924
+ " return nil",
925
+ "}",
926
+ "",
927
+ }, "\n"),
928
+ })
929
+ outputDir := filepath.Join(t.TempDir(), "output")
930
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
931
+ if err != nil {
932
+ t.Fatal(err.Error())
933
+ }
934
+
935
+ if _, err := comp.CompilePackages(context.Background(), "./world"); err != nil {
936
+ t.Fatal(err.Error())
937
+ }
938
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "sourcealiases", "world", "world.gs.ts")
939
+ content, err := os.ReadFile(outputFile)
940
+ if err != nil {
941
+ t.Fatal(err.Error())
942
+ }
943
+ text := string(content)
944
+ matches := regexp.MustCompile(`import \* as (\w+) from "@goscript/example\.test/sourcealiases/dep/block/index\.js"`).FindStringSubmatch(text)
945
+ if len(matches) != 2 {
946
+ t.Fatalf("missing dep/block import in generated output:\n%s", text)
947
+ }
948
+ if !strings.Contains(text, "return "+matches[1]+".ErrUnexpectedType") {
949
+ t.Fatalf("selector did not use the alias for its source package %q:\n%s", matches[1], text)
950
+ }
951
+ }
952
+
953
+ func TestCompilePackagesEmitsSideEffectImportsForInterfaceRegistry(t *testing.T) {
954
+ moduleDir := writePackageGraphFixture(t, map[string]string{
955
+ "go.mod": "module example.test/interface-registry\n\ngo 1.25.3\n",
956
+ "main.go": strings.Join([]string{
957
+ "package main",
958
+ "import \"example.test/interface-registry/dep\"",
959
+ "type localImpl struct{}",
960
+ "func (*localImpl) Ping() string { return \"pong\" }",
961
+ "func matchLocal(v any) bool {",
962
+ " switch v.(type) {",
963
+ " case Local:",
964
+ " return true",
965
+ " }",
966
+ " return false",
967
+ "}",
968
+ "func matchDep(v any) bool {",
969
+ " _, ok := v.(dep.Remote)",
970
+ " return ok",
971
+ "}",
972
+ "func main() { println(matchLocal(&localImpl{}), matchDep(nil)) }",
973
+ "",
974
+ }, "\n"),
975
+ "local.go": strings.Join([]string{
976
+ "package main",
977
+ "type Local interface { Ping() string }",
978
+ "",
979
+ }, "\n"),
980
+ "dep/dep.go": strings.Join([]string{
981
+ "package dep",
982
+ "type Remote interface { Remote() string }",
983
+ "",
984
+ }, "\n"),
985
+ })
986
+ outputDir := filepath.Join(t.TempDir(), "output")
987
+ comp, err := NewCompiler(&Config{
988
+ Dir: moduleDir,
989
+ OutputPath: outputDir,
990
+ AllDependencies: true,
991
+ }, nil, nil)
992
+ if err != nil {
993
+ t.Fatal(err.Error())
994
+ }
995
+
996
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
997
+ t.Fatal(err.Error())
998
+ }
999
+ mainContent, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "interface-registry", "main.gs.ts"))
1000
+ if err != nil {
1001
+ t.Fatal(err.Error())
1002
+ }
1003
+ mainText := string(mainContent)
1004
+ for _, want := range []string{
1005
+ "import * as dep from \"@goscript/example.test/interface-registry/dep/index.js\"",
1006
+ "import \"@goscript/example.test/interface-registry/dep/index.js\"",
1007
+ "import * as __goscript_local from \"./local.gs.ts\"",
1008
+ "import \"./local.gs.ts\"",
1009
+ "case $.typeAssert<Exclude<__goscript_local.Local, null>>(__goscriptTypeSwitchValue, \"main.Local\").ok",
1010
+ "$.typeAssertTuple<dep.Remote | null>(v, \"dep.Remote\")",
1011
+ } {
1012
+ if !strings.Contains(mainText, want) {
1013
+ t.Fatalf("missing %q in main output:\n%s", want, mainText)
1014
+ }
1015
+ }
1016
+
1017
+ depIndexContent, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "interface-registry", "dep", "index.ts"))
1018
+ if err != nil {
1019
+ t.Fatal(err.Error())
1020
+ }
1021
+ if !strings.Contains(string(depIndexContent), "import \"./dep.gs.ts\"") {
1022
+ t.Fatalf("missing interface side-effect import in dep index:\n%s", string(depIndexContent))
1023
+ }
1024
+ }
1025
+
1026
+ func TestCompilePackagesEmitsIndexAddressRefs(t *testing.T) {
1027
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1028
+ "go.mod": "module example.test/indexaddr\n\ngo 1.25.3\n",
1029
+ "main.go": strings.Join([]string{
1030
+ "package main",
1031
+ "type Item struct { N int }",
1032
+ "func set(ptr *int, value int) {",
1033
+ " *ptr = value",
1034
+ "}",
1035
+ "func Use(values []int, i int) int {",
1036
+ " set(&values[i], 9)",
1037
+ " return values[i]",
1038
+ "}",
1039
+ "func Items() []*Item {",
1040
+ " return []*Item{{N: 1}}",
1041
+ "}",
1042
+ "",
1043
+ }, "\n"),
1044
+ })
1045
+ outputDir := filepath.Join(t.TempDir(), "output")
1046
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1047
+ if err != nil {
1048
+ t.Fatal(err.Error())
1049
+ }
1050
+
1051
+ _, err = comp.CompilePackages(context.Background(), ".")
1052
+ if err != nil {
1053
+ t.Fatal(err.Error())
1054
+ }
1055
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "indexaddr", "main.gs.ts")
1056
+ content, err := os.ReadFile(outputFile)
1057
+ if err != nil {
1058
+ t.Fatal(err.Error())
1059
+ }
1060
+ text := string(content)
1061
+ if !strings.Contains(text, "set($.indexRef(values!, i), 9)") {
1062
+ t.Fatalf("missing index address reference:\n%s", text)
1063
+ }
1064
+ if !strings.Contains(text, "new Item({N: 1})") {
1065
+ t.Fatalf("missing elided pointer composite literal:\n%s", text)
1066
+ }
1067
+ }
1068
+
1069
+ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
1070
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1071
+ "go.mod": "module example.test/structs\n\ngo 1.25.3\n",
1072
+ "main.go": strings.Join([]string{
1073
+ "package main",
1074
+ "type Counter struct {",
1075
+ " // Value counts reads.",
1076
+ " Value int `json:\"value\"`",
1077
+ " ID ObjectID",
1078
+ "}",
1079
+ "type ObjectID int32",
1080
+ "func (c Counter) Read() int {",
1081
+ " return c.Value",
1082
+ "}",
1083
+ "func (c *Counter) Set(v int) {",
1084
+ " c.Value = v",
1085
+ "}",
1086
+ "func NewCounter() *Counter {",
1087
+ " return &Counter{Value: 3}",
1088
+ "}",
1089
+ "func main() {",
1090
+ " original := Counter{Value: 1}",
1091
+ "",
1092
+ " // Copy should stay readable in generated output.",
1093
+ " copy := original",
1094
+ " pointer := &original",
1095
+ " pointer.Set(2)",
1096
+ " NewCounter().Set(5)",
1097
+ " var iface any = pointer",
1098
+ " _, ok := iface.(*Counter)",
1099
+ " println(copy.Read(), original.Read(), ok)",
1100
+ "}",
1101
+ "",
1102
+ }, "\n"),
1103
+ })
1104
+ outputDir := filepath.Join(t.TempDir(), "output")
1105
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1106
+ if err != nil {
1107
+ t.Fatal(err.Error())
1108
+ }
1109
+
1110
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1111
+ t.Fatal(err.Error())
1112
+ }
1113
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "structs", "main.gs.ts")
1114
+ content, err := os.ReadFile(outputFile)
1115
+ if err != nil {
1116
+ t.Fatal(err.Error())
1117
+ }
1118
+ text := string(content)
1119
+ for _, want := range []string{
1120
+ "export class Counter",
1121
+ "// Value counts reads.\n\tpublic get Value(): number",
1122
+ "public clone(): Counter",
1123
+ "public Read(): number",
1124
+ "public Set(v: number): void",
1125
+ "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))",
1126
+ "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))\n\n\t// Copy should stay readable in generated output.\n\tlet copy",
1127
+ "let copy = $.markAsStructValue($.cloneStructValue(original.value))",
1128
+ "let pointer: Counter | $.VarRef<Counter> | null = original",
1129
+ "Counter.prototype.Set.call(pointer, 2)",
1130
+ "Counter.prototype.Set.call(NewCounter(), 5)",
1131
+ "let [, ok] = $.typeAssertTuple<Counter | $.VarRef<Counter> | null>(iface, { kind: $.TypeKind.Pointer, elemType: \"main.Counter\" })",
1132
+ "\"Value\": { type: { kind: $.TypeKind.Basic, name: \"int\" }, tag: \"json:\\\"value\\\"\" }",
1133
+ "\"ID\": { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.ObjectID\" }",
1134
+ } {
1135
+ if !strings.Contains(text, want) {
1136
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1137
+ }
1138
+ }
1139
+ }
1140
+
1141
+ func TestCompilePackagesClonesNestedStructFieldsWithCloneMethodCollision(t *testing.T) {
1142
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1143
+ "go.mod": "module example.test/nestedclone\n\ngo 1.25.3\n",
1144
+ "main.go": strings.Join([]string{
1145
+ "package main",
1146
+ "type Box struct { Value int }",
1147
+ "func (b *Box) clone() (*Box, error) {",
1148
+ " return &Box{Value: b.Value + 1}, nil",
1149
+ "}",
1150
+ "type Holder struct { Box Box }",
1151
+ "func copyHolder(h Holder) Holder { return h }",
1152
+ "func main() {",
1153
+ " _ = copyHolder(Holder{Box: Box{Value: 1}})",
1154
+ "}",
1155
+ "",
1156
+ }, "\n"),
1157
+ })
1158
+ outputDir := filepath.Join(t.TempDir(), "output")
1159
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1160
+ if err != nil {
1161
+ t.Fatal(err.Error())
1162
+ }
1163
+
1164
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1165
+ t.Fatal(err.Error())
1166
+ }
1167
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "nestedclone", "main.gs.ts"))
1168
+ if err != nil {
1169
+ t.Fatal(err.Error())
1170
+ }
1171
+ text := string(content)
1172
+ for _, want := range []string{
1173
+ "public __goscriptClone(): Box",
1174
+ "Box: $.varRef(init?.Box ? $.markAsStructValue($.cloneStructValue(init.Box)) : $.markAsStructValue(new Box()))",
1175
+ "Box: $.varRef($.markAsStructValue($.cloneStructValue(this._fields.Box.value)))",
1176
+ } {
1177
+ if !strings.Contains(text, want) {
1178
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1179
+ }
1180
+ }
1181
+ if strings.Contains(text, "init.Box.clone()") || strings.Contains(text, "this._fields.Box.value.clone()") {
1182
+ t.Fatalf("nested struct-field clone bypassed cloneStructValue:\n%s", text)
1183
+ }
1184
+ }
1185
+
1186
+ func TestCompilePackagesEmitsNestedPointerStorageAssertions(t *testing.T) {
1187
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1188
+ "go.mod": "module example.test/pointers\n\ngo 1.25.3\n",
1189
+ "main.go": strings.Join([]string{
1190
+ "package main",
1191
+ "func main() {",
1192
+ " var x int = 10",
1193
+ " p1 := &x",
1194
+ " p2 := &p1",
1195
+ " p3 := &p2",
1196
+ " ***p3 = 12",
1197
+ " println(x)",
1198
+ "}",
1199
+ "",
1200
+ }, "\n"),
1201
+ })
1202
+ outputDir := filepath.Join(t.TempDir(), "output")
1203
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1204
+ if err != nil {
1205
+ t.Fatal(err.Error())
1206
+ }
1207
+
1208
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1209
+ t.Fatal(err.Error())
1210
+ }
1211
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "pointers", "main.gs.ts")
1212
+ content, err := os.ReadFile(outputFile)
1213
+ if err != nil {
1214
+ t.Fatal(err.Error())
1215
+ }
1216
+ text := string(content)
1217
+ for _, want := range []string{
1218
+ "let x: $.VarRef<number> = $.varRef(10)",
1219
+ "let p1 = $.varRef(x)",
1220
+ "let p2 = $.varRef(p1)",
1221
+ "let p3 = p2",
1222
+ "$.pointerValue<$.VarRef<number> | null>($.pointerValue<$.VarRef<$.VarRef<number> | null> | null>(p3))!.value = 12",
1223
+ } {
1224
+ if !strings.Contains(text, want) {
1225
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1226
+ }
1227
+ }
1228
+ }
1229
+
1230
+ func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
1231
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1232
+ "go.mod": "module example.test/collections\n\ngo 1.25.3\n",
1233
+ "main.go": strings.Join([]string{
1234
+ "package main",
1235
+ "type MyInt int",
1236
+ "func (m MyInt) Double() int { return int(m) * 2 }",
1237
+ "type MySlice []int",
1238
+ "func (s *MySlice) Add(v int) { *s = append(*s, v) }",
1239
+ "func main() {",
1240
+ " arr := [3]int{1: 10}",
1241
+ " slice := make([]int, 0, 2)",
1242
+ " empty := []rune{}",
1243
+ " literal := []int{1, 2}",
1244
+ " literal = append(literal, 3)",
1245
+ " slice = append(slice, 5)",
1246
+ " slice[0] = arr[1]",
1247
+ " m := make(map[string]int)",
1248
+ " m[\"one\"] = 1",
1249
+ " value, ok := m[\"missing\"]",
1250
+ " text := \"hé\"",
1251
+ " var list MySlice",
1252
+ " list.Add(7)",
1253
+ " println(arr[1], slice[0], literal[2], len(slice), cap(slice), len(empty), value, ok, text[0], text[1], MyInt(5).Double(), len(list))",
1254
+ "}",
1255
+ "",
1256
+ }, "\n"),
1257
+ })
1258
+ outputDir := filepath.Join(t.TempDir(), "output")
1259
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1260
+ if err != nil {
1261
+ t.Fatal(err.Error())
1262
+ }
1263
+
1264
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1265
+ t.Fatal(err.Error())
1266
+ }
1267
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "collections", "main.gs.ts")
1268
+ content, err := os.ReadFile(outputFile)
1269
+ if err != nil {
1270
+ t.Fatal(err.Error())
1271
+ }
1272
+ text := string(content)
1273
+ for _, want := range []string{
1274
+ "export type MyInt = number",
1275
+ "export function MyInt_Double(m: MyInt): number",
1276
+ "export type MySlice = $.Slice<number>",
1277
+ "export function MySlice_Add(s: $.VarRef<MySlice> | null, v: number): void",
1278
+ "let arr = [0, 10, 0]",
1279
+ "let slice: $.Slice<number> = $.makeSlice<number>(0, 2, \"number\")",
1280
+ "let empty: $.Slice<number> = $.arrayToSlice<number>([])",
1281
+ "let literal: $.Slice<number> = $.arrayToSlice<number>([1, 2])",
1282
+ "literal = $.append(literal, 3)",
1283
+ "slice![0] = arr[1]",
1284
+ "let m: Map<string, number> | null = $.makeMap<string, number>()",
1285
+ "$.mapSet(m, \"one\", 1)",
1286
+ "let [value, ok] = $.mapGet(m, \"missing\", 0)",
1287
+ "slice![0]",
1288
+ "literal![2]",
1289
+ "let list: $.VarRef<MySlice> = $.varRef(null as MySlice)",
1290
+ "MySlice_Add(list, 7)",
1291
+ "$.indexStringOrBytes(text, 0)",
1292
+ "MyInt_Double(5)",
1293
+ } {
1294
+ if !strings.Contains(text, want) {
1295
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1296
+ }
1297
+ }
1298
+ }
1299
+
1300
+ func TestCompilePackagesWrapsAddressedMapRangeValue(t *testing.T) {
1301
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1302
+ "go.mod": "module example.test/maprange\n\ngo 1.25.3\n",
1303
+ "main.go": strings.Join([]string{
1304
+ "package main",
1305
+ "type Item struct { Values []string }",
1306
+ "func (i *Item) add(v string) { i.Values = append(i.Values, v) }",
1307
+ "func Apply(m map[int]Item, values []string) {",
1308
+ " for k, item := range m {",
1309
+ " for _, v := range values {",
1310
+ " item.add(v)",
1311
+ " }",
1312
+ " m[k] = item",
1313
+ " }",
1314
+ "}",
1315
+ "",
1316
+ }, "\n"),
1317
+ })
1318
+ outputDir := filepath.Join(t.TempDir(), "output")
1319
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1320
+ if err != nil {
1321
+ t.Fatal(err.Error())
1322
+ }
1323
+
1324
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1325
+ t.Fatal(err.Error())
1326
+ }
1327
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "maprange", "main.gs.ts")
1328
+ content, err := os.ReadFile(outputFile)
1329
+ if err != nil {
1330
+ t.Fatal(err.Error())
1331
+ }
1332
+ text := string(content)
1333
+ for _, want := range []string{
1334
+ "let item: $.VarRef<Item> = $.varRef($.markAsStructValue($.cloneStructValue(__goscriptRangeValue",
1335
+ "item.value.add(v)",
1336
+ "$.mapSet(m, k, $.markAsStructValue($.cloneStructValue(item.value)))",
1337
+ } {
1338
+ if !strings.Contains(text, want) {
1339
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1340
+ }
1341
+ }
1342
+ }
1343
+
1344
+ func TestCompilePackagesLowersPromotedNamedPrimitiveMethod(t *testing.T) {
1345
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1346
+ "go.mod": "module example.test/promotedprimitive\n\ngo 1.25.3\n",
1347
+ "main.go": strings.Join([]string{
1348
+ "package main",
1349
+ "import \"time\"",
1350
+ "type Lifetime struct { time.Duration }",
1351
+ "func Seconds(l Lifetime) float64 {",
1352
+ " return l.Seconds()",
1353
+ "}",
1354
+ "",
1355
+ }, "\n"),
1356
+ })
1357
+ outputDir := filepath.Join(t.TempDir(), "output")
1358
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1359
+ if err != nil {
1360
+ t.Fatal(err.Error())
1361
+ }
1362
+
1363
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1364
+ t.Fatal(err.Error())
1365
+ }
1366
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "promotedprimitive", "main.gs.ts")
1367
+ content, err := os.ReadFile(outputFile)
1368
+ if err != nil {
1369
+ t.Fatal(err.Error())
1370
+ }
1371
+ text := string(content)
1372
+ if !strings.Contains(text, "return time.Duration_Seconds(l.Duration)") {
1373
+ t.Fatalf("promoted named primitive method was not lowered through the owner function:\n%s", text)
1374
+ }
1375
+ if strings.Contains(text, ".Seconds()") {
1376
+ t.Fatalf("promoted named primitive method still used a JavaScript member call:\n%s", text)
1377
+ }
1378
+ }
1379
+
1380
+ func TestCompilePackagesMarksPackageFunctionVariablesAsync(t *testing.T) {
1381
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1382
+ "go.mod": "module example.test/packagefuncvar\n\ngo 1.25.3\n",
1383
+ "dep/dep.go": strings.Join([]string{
1384
+ "package dep",
1385
+ "func parse(raw string) (int, error) { return len(raw), nil }",
1386
+ "var Parse = parse",
1387
+ "",
1388
+ }, "\n"),
1389
+ "main.go": strings.Join([]string{
1390
+ "package main",
1391
+ "import \"example.test/packagefuncvar/dep\"",
1392
+ "func parse() (int, error) {",
1393
+ " return dep.Parse(\"turn\")",
1394
+ "}",
1395
+ "",
1396
+ }, "\n"),
1397
+ })
1398
+ outputDir := filepath.Join(t.TempDir(), "output")
1399
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1400
+ if err != nil {
1401
+ t.Fatal(err.Error())
1402
+ }
1403
+
1404
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1405
+ t.Fatal(err.Error())
1406
+ }
1407
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "packagefuncvar", "main.gs.ts")
1408
+ content, err := os.ReadFile(outputFile)
1409
+ if err != nil {
1410
+ t.Fatal(err.Error())
1411
+ }
1412
+ text := string(content)
1413
+ if !strings.Contains(text, "export async function parse(): globalThis.Promise<[number, $.GoError]>") {
1414
+ t.Fatalf("package function variable caller was not marked async:\n%s", text)
1415
+ }
1416
+ if !strings.Contains(text, "return await dep.Parse!(\"turn\")") {
1417
+ t.Fatalf("package function variable call was not awaited:\n%s", text)
1418
+ }
1419
+ }
1420
+
1421
+ func TestCompilePackagesAwaitsImportedAsyncMethodsAndFunctions(t *testing.T) {
1422
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1423
+ "go.mod": "module example.test/importedasync\n\ngo 1.25.3\n",
1424
+ "dep/dep.go": strings.Join([]string{
1425
+ "package dep",
1426
+ "type Host struct{ ch chan struct{} }",
1427
+ "func (h *Host) Snapshot() (int, error) { <-h.ch; return 1, nil }",
1428
+ "func Read(h *Host) (int, error) { return h.Snapshot() }",
1429
+ "",
1430
+ }, "\n"),
1431
+ "dep/dep_test.go": strings.Join([]string{
1432
+ "package dep",
1433
+ "import \"testing\"",
1434
+ "func TestHost(t *testing.T) {}",
1435
+ "",
1436
+ }, "\n"),
1437
+ "main.go": strings.Join([]string{
1438
+ "package main",
1439
+ "import \"example.test/importedasync/dep\"",
1440
+ "func UseMethod(h *dep.Host) (int, error) {",
1441
+ " return h.Snapshot()",
1442
+ "}",
1443
+ "func UseFunction(h *dep.Host) (int, error) {",
1444
+ " return dep.Read(h)",
1445
+ "}",
1446
+ "",
1447
+ }, "\n"),
1448
+ "main_test.go": strings.Join([]string{
1449
+ "package main",
1450
+ "import \"testing\"",
1451
+ "func TestUse(t *testing.T) {}",
1452
+ "",
1453
+ }, "\n"),
1454
+ })
1455
+ outputDir := filepath.Join(t.TempDir(), "output")
1456
+ service := NewCompileService()
1457
+ _, err := service.Compile(context.Background(), &CompileRequest{
1458
+ Patterns: []string{".", "./dep"},
1459
+ Dir: moduleDir,
1460
+ OutputPath: outputDir,
1461
+ DependencyMode: DependencyModeAll,
1462
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
1463
+ Tests: true,
1464
+ AllDependencies: true,
1465
+ })
1466
+ if err != nil {
1467
+ t.Fatal(err.Error())
1468
+ }
1469
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "importedasync", "main.gs.ts")
1470
+ content, err := os.ReadFile(outputFile)
1471
+ if err != nil {
1472
+ t.Fatal(err.Error())
1473
+ }
1474
+ text := string(content)
1475
+ if !strings.Contains(text, "export async function UseMethod") {
1476
+ t.Fatalf("imported async method caller was not marked async:\n%s", text)
1477
+ }
1478
+ if !strings.Contains(text, "return await dep.Host.prototype.Snapshot.call(h)") {
1479
+ t.Fatalf("imported async method call was not awaited:\n%s", text)
1480
+ }
1481
+ if !strings.Contains(text, "export async function UseFunction") {
1482
+ t.Fatalf("imported async function caller was not marked async:\n%s", text)
1483
+ }
1484
+ if !strings.Contains(text, "return await dep.Read(h)") {
1485
+ t.Fatalf("imported async function call was not awaited:\n%s", text)
1486
+ }
1487
+ }
1488
+
1489
+ func TestCompilePackagesCastsConvertedTupleCallSpreads(t *testing.T) {
1490
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1491
+ "go.mod": "module example.test/tuplecallspread\n\ngo 1.25.3\n",
1492
+ "main.go": strings.Join([]string{
1493
+ "package main",
1494
+ "type Resolver interface { Resolve() }",
1495
+ "type relay struct{}",
1496
+ "func (*relay) Resolve() {}",
1497
+ "func NewRelay() (*relay, error) { return &relay{}, nil }",
1498
+ "func R(res Resolver, err error) ([]Resolver, error) {",
1499
+ " if err != nil { return nil, err }",
1500
+ " return []Resolver{res}, nil",
1501
+ "}",
1502
+ "func Use() ([]Resolver, error) {",
1503
+ " return R(NewRelay())",
1504
+ "}",
1505
+ "",
1506
+ }, "\n"),
1507
+ })
1508
+ outputDir := filepath.Join(t.TempDir(), "output")
1509
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1510
+ if err != nil {
1511
+ t.Fatal(err.Error())
1512
+ }
1513
+
1514
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1515
+ t.Fatal(err.Error())
1516
+ }
1517
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "tuplecallspread", "main.gs.ts")
1518
+ content, err := os.ReadFile(outputFile)
1519
+ if err != nil {
1520
+ t.Fatal(err.Error())
1521
+ }
1522
+ text := string(content)
1523
+ if !strings.Contains(text, "return R(...(() => {") ||
1524
+ !strings.Contains(text, "] as [Resolver | null, $.GoError]") {
1525
+ t.Fatalf("converted tuple call spread was not cast as a TypeScript tuple:\n%s", text)
1526
+ }
1527
+ }
1528
+
1529
+ func TestCompilePackagesPreservesBlankNamedResultSlots(t *testing.T) {
1530
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1531
+ "go.mod": "module example.test/blankresult\n\ngo 1.25.3\n",
1532
+ "main.go": strings.Join([]string{
1533
+ "package main",
1534
+ "func values() (first, second bool, _ error) {",
1535
+ " first = true",
1536
+ " return",
1537
+ "}",
1538
+ "",
1539
+ }, "\n"),
1540
+ })
1541
+ outputDir := filepath.Join(t.TempDir(), "output")
1542
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1543
+ if err != nil {
1544
+ t.Fatal(err.Error())
1545
+ }
1546
+
1547
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1548
+ t.Fatal(err.Error())
1549
+ }
1550
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "blankresult", "main.gs.ts")
1551
+ content, err := os.ReadFile(outputFile)
1552
+ if err != nil {
1553
+ t.Fatal(err.Error())
1554
+ }
1555
+ text := string(content)
1556
+ if !strings.Contains(text, "return [first, second, null as $.GoError]") {
1557
+ t.Fatalf("blank named result slot was not preserved in naked return:\n%s", text)
1558
+ }
1559
+ }
1560
+
1561
+ func TestCompilePackagesCastsGenericMethodResultAssignments(t *testing.T) {
1562
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1563
+ "go.mod": "module example.test/genericmethodresult\n\ngo 1.25.3\n",
1564
+ "main.go": strings.Join([]string{
1565
+ "package main",
1566
+ "import (",
1567
+ " \"errors\"",
1568
+ " \"sync/atomic\"",
1569
+ ")",
1570
+ "func load() error {",
1571
+ " var ptr atomic.Pointer[error]",
1572
+ " err := errors.New(\"boom\")",
1573
+ " ptr.Store(&err)",
1574
+ " if errp := ptr.Load(); errp != nil {",
1575
+ " return *errp",
1576
+ " }",
1577
+ " return nil",
1578
+ "}",
1579
+ "",
1580
+ }, "\n"),
1581
+ })
1582
+ outputDir := filepath.Join(t.TempDir(), "output")
1583
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1584
+ if err != nil {
1585
+ t.Fatal(err.Error())
1586
+ }
1587
+
1588
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1589
+ t.Fatal(err.Error())
1590
+ }
1591
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "genericmethodresult", "main.gs.ts")
1592
+ content, err := os.ReadFile(outputFile)
1593
+ if err != nil {
1594
+ t.Fatal(err.Error())
1595
+ }
1596
+ text := string(content)
1597
+ if !strings.Contains(text, "(ptr.value.Load() as $.VarRef<$.GoError> | null)") {
1598
+ t.Fatalf("generic method result assignment was not cast to the target type:\n%s", text)
1599
+ }
1600
+ }
1601
+
1602
+ func TestCompilePackagesPreservesFloatConversionLiterals(t *testing.T) {
1603
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1604
+ "go.mod": "module example.test/floatconv\n\ngo 1.25.3\n",
1605
+ "main.go": strings.Join([]string{
1606
+ "package main",
1607
+ "import \"math\"",
1608
+ "func value() float64 {",
1609
+ " return math.Pow(float64(0.69314718056), 2)",
1610
+ "}",
1611
+ "",
1612
+ }, "\n"),
1613
+ })
1614
+ outputDir := filepath.Join(t.TempDir(), "output")
1615
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1616
+ if err != nil {
1617
+ t.Fatal(err.Error())
1618
+ }
1619
+
1620
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1621
+ t.Fatal(err.Error())
1622
+ }
1623
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "floatconv", "main.gs.ts")
1624
+ content, err := os.ReadFile(outputFile)
1625
+ if err != nil {
1626
+ t.Fatal(err.Error())
1627
+ }
1628
+ text := string(content)
1629
+ if strings.Contains(text, "$.int(0.69314718056)") {
1630
+ t.Fatalf("float64 literal conversion was truncated through $.int:\n%s", text)
1631
+ }
1632
+ if !strings.Contains(text, "math.Pow(0.69314718056, 2)") {
1633
+ t.Fatalf("missing direct float literal conversion:\n%s", text)
1634
+ }
1635
+ }
1636
+
1637
+ func TestCompilePackagesLowersStringOrderingThroughRuntime(t *testing.T) {
1638
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1639
+ "go.mod": "module example.test/stringorder\n\ngo 1.25.3\n",
1640
+ "main.go": strings.Join([]string{
1641
+ "package main",
1642
+ "func less(left, right []byte) bool {",
1643
+ " return string(left) < string(right)",
1644
+ "}",
1645
+ "func ordered(left, right string) bool {",
1646
+ " return left <= right",
1647
+ "}",
1648
+ "",
1649
+ }, "\n"),
1650
+ })
1651
+ outputDir := filepath.Join(t.TempDir(), "output")
1652
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1653
+ if err != nil {
1654
+ t.Fatal(err.Error())
1655
+ }
1656
+
1657
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1658
+ t.Fatal(err.Error())
1659
+ }
1660
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "stringorder", "main.gs.ts")
1661
+ content, err := os.ReadFile(outputFile)
1662
+ if err != nil {
1663
+ t.Fatal(err.Error())
1664
+ }
1665
+ text := string(content)
1666
+ for _, want := range []string{
1667
+ "$.stringCompare(left, right) < 0",
1668
+ "$.stringCompare(left, right) <= 0",
1669
+ } {
1670
+ if !strings.Contains(text, want) {
1671
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1672
+ }
1673
+ }
1674
+ }
1675
+
1676
+ func TestCompilePackagesEmitsInterfacesMethodValuesTypeSwitchesAndFunctionAssertions(t *testing.T) {
1677
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1678
+ "go.mod": "module example.test/interfaces\n\ngo 1.25.3\n",
1679
+ "main.go": strings.Join([]string{
1680
+ "package main",
1681
+ "type Greeter func(name string) string",
1682
+ "func greet(name string) string { return \"hello \" + name }",
1683
+ "type Reader interface { Read() string }",
1684
+ "type Closer interface { Close() string }",
1685
+ "type ReadCloser interface { Reader; Closer }",
1686
+ "type Counter struct { Value int }",
1687
+ "func (c *Counter) Inc() { c.Value++ }",
1688
+ "func (c Counter) Read() string { return \"read\" }",
1689
+ "func (c Counter) Close() string { return \"close\" }",
1690
+ "func call(fn func()) { fn() }",
1691
+ "func main() {",
1692
+ " counter := &Counter{}",
1693
+ " call(counter.Inc)",
1694
+ " var rc ReadCloser = counter",
1695
+ " _, ok := rc.(ReadCloser)",
1696
+ " var i any = Greeter(greet)",
1697
+ " fn, ok := i.(Greeter)",
1698
+ " var l any = (*struct { Name string })(nil)",
1699
+ " _, ok2 := l.(*struct { Name string })",
1700
+ " switch v := rc.(type) {",
1701
+ " case ReadCloser:",
1702
+ " println(v.Read(), fn(\"gopher\"), ok, ok2)",
1703
+ " }",
1704
+ "}",
1705
+ "",
1706
+ }, "\n"),
1707
+ })
1708
+ outputDir := filepath.Join(t.TempDir(), "output")
1709
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1710
+ if err != nil {
1711
+ t.Fatal(err.Error())
1712
+ }
1713
+
1714
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1715
+ t.Fatal(err.Error())
1716
+ }
1717
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "interfaces", "main.gs.ts")
1718
+ content, err := os.ReadFile(outputFile)
1719
+ if err != nil {
1720
+ t.Fatal(err.Error())
1721
+ }
1722
+ text := string(content)
1723
+ for _, want := range []string{
1724
+ "export type Greeter = ((name: string) => string | globalThis.Promise<string>) | null",
1725
+ "export type ReadCloser = {",
1726
+ "Read(): string",
1727
+ "Close(): string",
1728
+ "$.registerInterfaceType(\n\t\"main.ReadCloser\"",
1729
+ "((__receiver) => () => __receiver.Inc())($.pointerValue<Counter>(counter))",
1730
+ "$.namedFunction(greet, \"main.Greeter\")",
1731
+ "$.interfaceValue<any>(null, \"*struct{Name string}\")",
1732
+ "elemType: { kind: $.TypeKind.Struct, methods: [], fields: {\"Name\": { kind: $.TypeKind.Basic, name: \"string\" }} }",
1733
+ "let fn = __goscriptTuple",
1734
+ "switch (true)",
1735
+ "case $.typeAssert<Exclude<ReadCloser, null>>(__goscriptTypeSwitchValue, \"main.ReadCloser\").ok",
1736
+ "let v: Exclude<ReadCloser, null> = $.typeAssert<Exclude<ReadCloser, null>>(__goscriptTypeSwitchValue, \"main.ReadCloser\").value",
1737
+ } {
1738
+ if !strings.Contains(text, want) {
1739
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1740
+ }
1741
+ }
1742
+ }
1743
+
1744
+ func TestCompilePackagesAssertsInterfaceMethodReceivers(t *testing.T) {
1745
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1746
+ "go.mod": "module example.test/interface-receivers\n\ngo 1.25.3\n",
1747
+ "main.go": strings.Join([]string{
1748
+ "package main",
1749
+ "type FileInfo interface { Name() string }",
1750
+ "type fileInfo struct { name string }",
1751
+ "func (f fileInfo) Name() string { return f.name }",
1752
+ "func stat() (FileInfo, error) { return fileInfo{name: \"demo\"}, nil }",
1753
+ "func main() {",
1754
+ " info, err := stat()",
1755
+ " if err == nil {",
1756
+ " println(info.Name())",
1757
+ " }",
1758
+ "}",
1759
+ "",
1760
+ }, "\n"),
1761
+ })
1762
+ outputDir := filepath.Join(t.TempDir(), "output")
1763
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1764
+ if err != nil {
1765
+ t.Fatal(err.Error())
1766
+ }
1767
+
1768
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1769
+ t.Fatal(err.Error())
1770
+ }
1771
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "interface-receivers", "main.gs.ts")
1772
+ content, err := os.ReadFile(outputFile)
1773
+ if err != nil {
1774
+ t.Fatal(err.Error())
1775
+ }
1776
+ text := string(content)
1777
+ for _, want := range []string{
1778
+ "export type FileInfo = {",
1779
+ "$.println($.pointerValue<Exclude<FileInfo, null>>(info).Name())",
1780
+ } {
1781
+ if !strings.Contains(text, want) {
1782
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1783
+ }
1784
+ }
1785
+ }
1786
+
1787
+ func TestCompilePackagesUsesNonNilInterfaceTypeSwitchCaseVarRefs(t *testing.T) {
1788
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1789
+ "go.mod": "module example.test/interface-switch-varref\n\ngo 1.25.3\n",
1790
+ "main.go": strings.Join([]string{
1791
+ "package main",
1792
+ "type rawConn interface { Control() error }",
1793
+ "type tcpConn interface {",
1794
+ " SyscallConn() (rawConn, error)",
1795
+ " SetLinger(int) error",
1796
+ "}",
1797
+ "type Conn struct { c rawConn }",
1798
+ "func keep(*tcpConn) {}",
1799
+ "func NewConn(c any) (*Conn, error) {",
1800
+ " cc := &Conn{}",
1801
+ " var err error",
1802
+ " switch c := c.(type) {",
1803
+ " case tcpConn:",
1804
+ " keep(&c)",
1805
+ " cc.c, err = c.SyscallConn()",
1806
+ " }",
1807
+ " return cc, err",
1808
+ "}",
1809
+ "",
1810
+ }, "\n"),
1811
+ })
1812
+ outputDir := filepath.Join(t.TempDir(), "output")
1813
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1814
+ if err != nil {
1815
+ t.Fatal(err.Error())
1816
+ }
1817
+
1818
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1819
+ t.Fatal(err.Error())
1820
+ }
1821
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "interface-switch-varref", "main.gs.ts")
1822
+ content, err := os.ReadFile(outputFile)
1823
+ if err != nil {
1824
+ t.Fatal(err.Error())
1825
+ }
1826
+ text := string(content)
1827
+ for _, want := range []string{
1828
+ "export type tcpConn = {",
1829
+ "case $.typeAssert<Exclude<tcpConn, null>>(__goscriptTypeSwitchValue, \"main.tcpConn\").ok",
1830
+ "let c: $.VarRef<Exclude<tcpConn, null>> = $.varRef($.typeAssert<Exclude<tcpConn, null>>(__goscriptTypeSwitchValue, \"main.tcpConn\").value)",
1831
+ "$.pointerValue<Exclude<tcpConn, null>>(c.value).SyscallConn()",
1832
+ } {
1833
+ if !strings.Contains(text, want) {
1834
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1835
+ }
1836
+ }
1837
+ }
1838
+
1839
+ func TestCompilePackagesBoxesTypedNilInterfaceValues(t *testing.T) {
1840
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1841
+ "go.mod": "module example.test/typed-nil-interface\n\ngo 1.25.3\n",
1842
+ "main.go": strings.Join([]string{
1843
+ "package main",
1844
+ "type Animal interface { Name() string }",
1845
+ "type Dog struct { name string }",
1846
+ "func (d *Dog) Name() string {",
1847
+ " if d == nil {",
1848
+ " return \"unknown dog\"",
1849
+ " }",
1850
+ " return d.name",
1851
+ "}",
1852
+ "func FindDog() *Dog { return nil }",
1853
+ "func FindAnimal() Animal { return Animal(FindDog()) }",
1854
+ "func main() {",
1855
+ " animal := FindAnimal()",
1856
+ " println(animal.Name())",
1857
+ " var dog *Dog = nil",
1858
+ " var a Animal = dog",
1859
+ " println(a == nil)",
1860
+ "}",
1861
+ "",
1862
+ }, "\n"),
1863
+ })
1864
+ outputDir := filepath.Join(t.TempDir(), "output")
1865
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1866
+ if err != nil {
1867
+ t.Fatal(err.Error())
1868
+ }
1869
+
1870
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1871
+ t.Fatal(err.Error())
1872
+ }
1873
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "typed-nil-interface", "main.gs.ts")
1874
+ content, err := os.ReadFile(outputFile)
1875
+ if err != nil {
1876
+ t.Fatal(err.Error())
1877
+ }
1878
+ text := string(content)
1879
+ for _, want := range []string{
1880
+ "return $.interfaceValue<Animal | null>(FindDog(), \"*main.Dog\")",
1881
+ "$.println($.pointerValue<Exclude<Animal, null>>(animal).Name())",
1882
+ "let a: Animal | null = $.interfaceValue<Animal | null>(dog, \"*main.Dog\")",
1883
+ } {
1884
+ if !strings.Contains(text, want) {
1885
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1886
+ }
1887
+ }
1888
+ }
1889
+
1890
+ func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T) {
1891
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1892
+ "go.mod": "module example.test/generics\n\ngo 1.25.3\n",
1893
+ "main.go": strings.Join([]string{
1894
+ "package main",
1895
+ "type Stringer interface { String() string }",
1896
+ "type MyInt int",
1897
+ "func (m MyInt) String() string { return \"int\" }",
1898
+ "type Box[T any] struct { value T }",
1899
+ "func (b Box[T]) Get() T { return b.value }",
1900
+ "func NewBox[T any](value T) Box[T] { return Box[T]{value: value} }",
1901
+ "type Set[T comparable] map[T]struct{}",
1902
+ "func ZeroValue[T Stringer]() T {",
1903
+ " var zero T",
1904
+ " return zero",
1905
+ "}",
1906
+ "func CallString[T Stringer](v T) string { return v.String() }",
1907
+ "func Sum[T Stringer](vals ...T) T {",
1908
+ " var zero T",
1909
+ " return zero",
1910
+ "}",
1911
+ "func Copy[T any](vals ...T) []T {",
1912
+ " return append([]T{}, vals...)",
1913
+ "}",
1914
+ "func main() {",
1915
+ " box := NewBox(7)",
1916
+ " println(box.Get())",
1917
+ " seen := make(Set[int])",
1918
+ " seen[1] = struct{}{}",
1919
+ " zero := ZeroValue[MyInt]()",
1920
+ " println(CallString(zero))",
1921
+ " sum := Sum[MyInt]()",
1922
+ " println(CallString(sum))",
1923
+ "}",
1924
+ "",
1925
+ }, "\n"),
1926
+ })
1927
+ outputDir := filepath.Join(t.TempDir(), "output")
1928
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1929
+ if err != nil {
1930
+ t.Fatal(err.Error())
1931
+ }
1932
+
1933
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1934
+ t.Fatal(err.Error())
1935
+ }
1936
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "generics", "main.gs.ts")
1937
+ content, err := os.ReadFile(outputFile)
1938
+ if err != nil {
1939
+ t.Fatal(err.Error())
1940
+ }
1941
+ text := string(content)
1942
+ for _, want := range []string{
1943
+ "public Get(): any",
1944
+ "export function NewBox(__typeArgs: $.GenericTypeArgs | undefined, value: any): Box",
1945
+ "export function ZeroValue(__typeArgs: $.GenericTypeArgs | undefined): any",
1946
+ "export function CallString(__typeArgs: $.GenericTypeArgs | undefined, v: any): string",
1947
+ "export function Sum<T>(__typeArgs: $.GenericTypeArgs | undefined, vals: $.Slice<T>): any",
1948
+ "export function Copy<T>(__typeArgs: $.GenericTypeArgs | undefined, vals: $.Slice<T>): $.Slice<T>",
1949
+ "return $.append($.arrayToSlice<T>([]), ...(vals ?? []))",
1950
+ "let seen: Set = $.makeMap<number, {}>()",
1951
+ "$.mapSet(seen, 1, {})",
1952
+ "$.genericZero(__typeArgs, \"T\", null)",
1953
+ "$.callGenericMethod(__typeArgs, \"T\", \"String\", v)",
1954
+ "ZeroValue({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)($.pointerValue(receiver), ...args)} }})",
1955
+ "CallString({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)($.pointerValue(receiver), ...args)} }}, zero)",
1956
+ "Sum({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)($.pointerValue(receiver), ...args)} }}, null)",
1957
+ } {
1958
+ if !strings.Contains(text, want) {
1959
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1960
+ }
1961
+ }
1962
+ }
1963
+
1964
+ func TestCompilePackagesAttachesFunctionLiteralTypeInfo(t *testing.T) {
1965
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1966
+ "go.mod": "module example.test/function-type-info\n\ngo 1.25.3\n",
1967
+ "main.go": strings.Join([]string{
1968
+ "package main",
1969
+ "type Callback func(value int) string",
1970
+ "func call(cb Callback) string {",
1971
+ " return cb(1)",
1972
+ "}",
1973
+ "func main() {",
1974
+ " fn := func(value int) string {",
1975
+ " return \"\"",
1976
+ " }",
1977
+ " var cb Callback = nil",
1978
+ " _ = fn",
1979
+ " _ = cb",
1980
+ " _ = call(fn)",
1981
+ "}",
1982
+ "",
1983
+ }, "\n"),
1984
+ })
1985
+ outputDir := filepath.Join(t.TempDir(), "output")
1986
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1987
+ if err != nil {
1988
+ t.Fatal(err.Error())
1989
+ }
1990
+
1991
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1992
+ t.Fatal(err.Error())
1993
+ }
1994
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "function-type-info", "main.gs.ts")
1995
+ content, err := os.ReadFile(outputFile)
1996
+ if err != nil {
1997
+ t.Fatal(err.Error())
1998
+ }
1999
+ text := string(content)
2000
+ for _, want := range []string{
2001
+ "export type Callback = ((value: number) => string | globalThis.Promise<string>) | null",
2002
+ "export async function call(cb: ((value: number) => string | globalThis.Promise<string>) | null): globalThis.Promise<string> {\n\treturn await cb!(1)",
2003
+ "$.functionValue((value: number): string => {",
2004
+ "kind: $.TypeKind.Function",
2005
+ "params: [{ kind: $.TypeKind.Basic, name: \"int\" }]",
2006
+ "results: [{ kind: $.TypeKind.Basic, name: \"string\" }]",
2007
+ } {
2008
+ if !strings.Contains(text, want) {
2009
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2010
+ }
2011
+ }
2012
+ }
2013
+
2014
+ func TestCompilePackagesEmitsRecursiveFunctionTypeInfo(t *testing.T) {
2015
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2016
+ "go.mod": "module example.test/recursive-function-type\n\ngo 1.25.3\n",
2017
+ "main.go": strings.Join([]string{
2018
+ "package main",
2019
+ "type Handler func(Handler) Handler",
2020
+ "type Holder struct { Next Handler }",
2021
+ "func main() { _ = Holder{} }",
2022
+ "",
2023
+ }, "\n"),
2024
+ })
2025
+ outputDir := filepath.Join(t.TempDir(), "output")
2026
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2027
+ if err != nil {
2028
+ t.Fatal(err.Error())
2029
+ }
2030
+
2031
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2032
+ t.Fatal(err.Error())
2033
+ }
2034
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "recursive-function-type", "main.gs.ts")
2035
+ content, err := os.ReadFile(outputFile)
2036
+ if err != nil {
2037
+ t.Fatal(err.Error())
2038
+ }
2039
+ text := string(content)
2040
+ for _, want := range []string{
2041
+ "export type Handler = ((_p0: ((_p0: Handler | null) => Handler | null | globalThis.Promise<Handler | null>) | null) => Handler | null | globalThis.Promise<Handler | null>) | null",
2042
+ "\"Next\": ({ kind: $.TypeKind.Function, name: \"main.Handler\"",
2043
+ "params: [{ kind: $.TypeKind.Function, params: [], results: [] }]",
2044
+ "results: [{ kind: $.TypeKind.Function, params: [], results: [] }]",
2045
+ } {
2046
+ if !strings.Contains(text, want) {
2047
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2048
+ }
2049
+ }
2050
+ }
2051
+
2052
+ func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
2053
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2054
+ "go.mod": "module example.test/variadic\n\ngo 1.25.3\n",
2055
+ "main.go": strings.Join([]string{
2056
+ "package main",
2057
+ "type Collector func(label string, parts ...string) string",
2058
+ "type Joiner interface {",
2059
+ " Join(parts ...string) string",
2060
+ "}",
2061
+ "type Path struct{}",
2062
+ "func collect(label string, parts ...string) string {",
2063
+ " for _, part := range parts {",
2064
+ " if part == \"\" {",
2065
+ " return label",
2066
+ " }",
2067
+ " }",
2068
+ " return label + string(rune(len(parts)+'0'))",
2069
+ "}",
2070
+ "func maybeErr(parts ...string) error { return nil }",
2071
+ "func (Path) Join(parts ...string) string {",
2072
+ " return collect(\"method\", parts...)",
2073
+ "}",
2074
+ "func main() {",
2075
+ " parts := []string{\"a\", \"b\"}",
2076
+ " collect(\"none\")",
2077
+ " collect(\"two\", \"a\", \"b\")",
2078
+ " collect(\"spread\", parts...)",
2079
+ " parts = append(parts, \"c\", \"d\")",
2080
+ " maybeErr(\"ok\")",
2081
+ " var fn Collector = collect",
2082
+ " fn(\"fn\", \"x\")",
2083
+ " var joiner Joiner = Path{}",
2084
+ " joiner.Join(\"q\", \"r\")",
2085
+ "}",
2086
+ "",
2087
+ }, "\n"),
2088
+ })
2089
+ outputDir := filepath.Join(t.TempDir(), "output")
2090
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2091
+ if err != nil {
2092
+ t.Fatal(err.Error())
2093
+ }
2094
+
2095
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2096
+ t.Fatal(err.Error())
2097
+ }
2098
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "variadic", "main.gs.ts")
2099
+ content, err := os.ReadFile(outputFile)
2100
+ if err != nil {
2101
+ t.Fatal(err.Error())
2102
+ }
2103
+ text := string(content)
2104
+ for _, want := range []string{
2105
+ "export type Collector = ((label: string, parts: $.Slice<string>) => string | globalThis.Promise<string>) | null",
2106
+ "Join(parts: $.Slice<string>): string",
2107
+ "export function collect(label: string, parts: $.Slice<string>): string",
2108
+ "let part = __goscriptRangeTarget0![__rangeIndex]",
2109
+ "export function maybeErr(parts: $.Slice<string>): $.GoError",
2110
+ "public Join(parts: $.Slice<string>): string",
2111
+ "collect(\"none\", null)",
2112
+ "collect(\"two\", $.arrayToSlice<string>([\"a\", \"b\"]))",
2113
+ "collect(\"spread\", parts)",
2114
+ "$.append(parts, \"c\", \"d\")",
2115
+ "maybeErr($.arrayToSlice<string>([\"ok\"]))",
2116
+ "fn!(\"fn\", $.arrayToSlice<string>([\"x\"]))",
2117
+ "$.pointerValue<Exclude<Joiner, null>>(joiner).Join($.arrayToSlice<string>([\"q\", \"r\"]))",
2118
+ } {
2119
+ if !strings.Contains(text, want) {
2120
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2121
+ }
2122
+ }
2123
+ }
2124
+
2125
+ func TestCompilePackagesPacksVariadicCallsInGeneratedSubpackage(t *testing.T) {
2126
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2127
+ "go.mod": "module example.test/variadic-subpackage\n\ngo 1.25.3\n",
2128
+ "json/json.go": strings.Join([]string{
2129
+ "package json",
2130
+ "import \"fmt\"",
2131
+ "type State struct { err error }",
2132
+ "func (s *State) SetErrorf(format string, a ...any) {",
2133
+ " s.err = fmt.Errorf(format, a...)",
2134
+ "}",
2135
+ "func (s *State) Read(key string) {",
2136
+ " s.SetErrorf(\"bad %q\", key)",
2137
+ "}",
2138
+ "",
2139
+ }, "\n"),
2140
+ })
2141
+ outputDir := filepath.Join(t.TempDir(), "output")
2142
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2143
+ if err != nil {
2144
+ t.Fatal(err.Error())
2145
+ }
2146
+
2147
+ if _, err := comp.CompilePackages(context.Background(), "./json"); err != nil {
2148
+ t.Fatal(err.Error())
2149
+ }
2150
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "variadic-subpackage", "json", "json.gs.ts")
2151
+ content, err := os.ReadFile(outputFile)
2152
+ if err != nil {
2153
+ t.Fatal(err.Error())
2154
+ }
2155
+ text := string(content)
2156
+ want := "State.prototype.SetErrorf.call(s, \"bad %q\", $.arrayToSlice<any>([key]))"
2157
+ if !strings.Contains(text, want) {
2158
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2159
+ }
2160
+ if strings.Contains(text, "$.pointerValue<State>(s).SetErrorf(\"bad %q\", key)") {
2161
+ t.Fatalf("generated override subpackage call was not packed:\n%s", text)
2162
+ }
2163
+ }
2164
+
2165
+ func TestCompilePackagesImportsSelectedExternalFieldTypes(t *testing.T) {
2166
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2167
+ "go.mod": "module example.test/selected-field-import\n\ngo 1.25.3\n",
2168
+ "dep/dep.go": strings.Join([]string{
2169
+ "package dep",
2170
+ "type URL struct { Path string }",
2171
+ "",
2172
+ }, "\n"),
2173
+ "api/api.go": strings.Join([]string{
2174
+ "package api",
2175
+ "import \"example.test/selected-field-import/dep\"",
2176
+ "type Request struct { URL *dep.URL }",
2177
+ "",
2178
+ }, "\n"),
2179
+ "main.go": strings.Join([]string{
2180
+ "package main",
2181
+ "import \"example.test/selected-field-import/api\"",
2182
+ "func requestPath(r *api.Request) string {",
2183
+ " return r.URL.Path",
2184
+ "}",
2185
+ "",
2186
+ }, "\n"),
2187
+ })
2188
+ outputDir := filepath.Join(t.TempDir(), "output")
2189
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir, AllDependencies: true}, nil, nil)
2190
+ if err != nil {
2191
+ t.Fatal(err.Error())
2192
+ }
2193
+
2194
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2195
+ t.Fatal(err.Error())
2196
+ }
2197
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "selected-field-import", "main.gs.ts")
2198
+ content, err := os.ReadFile(outputFile)
2199
+ if err != nil {
2200
+ t.Fatal(err.Error())
2201
+ }
2202
+ text := string(content)
2203
+ for _, want := range []string{
2204
+ "import * as dep from \"@goscript/example.test/selected-field-import/dep/index.js\"",
2205
+ "$.pointerValue<dep.URL>($.pointerValue<api.Request>(r).URL).Path",
2206
+ } {
2207
+ if !strings.Contains(text, want) {
2208
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2209
+ }
2210
+ }
2211
+ }
2212
+
2213
+ func TestCompilePackagesErasesUnavailableOverrideFieldTypes(t *testing.T) {
2214
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2215
+ "go.mod": "module example.test/override-field-type\n\ngo 1.25.3\n",
2216
+ "dep/dep.go": strings.Join([]string{
2217
+ "package dep",
2218
+ "type URL struct { Path string }",
2219
+ "",
2220
+ }, "\n"),
2221
+ "api/api.go": strings.Join([]string{
2222
+ "package api",
2223
+ "import \"example.test/override-field-type/dep\"",
2224
+ "type Request struct { URL *dep.URL }",
2225
+ "",
2226
+ }, "\n"),
2227
+ "main.go": strings.Join([]string{
2228
+ "package main",
2229
+ "import \"example.test/override-field-type/api\"",
2230
+ "func requestPath(r *api.Request) string {",
2231
+ " return r.URL.Path",
2232
+ "}",
2233
+ "",
2234
+ }, "\n"),
2235
+ })
2236
+ overrideDir := filepath.Join(t.TempDir(), "gs")
2237
+ writeFixtureFile(t, overrideDir, "example.test/override-field-type/api/index.ts", strings.Join([]string{
2238
+ "export class Request {",
2239
+ " public URL: any = null",
2240
+ "}",
2241
+ "",
2242
+ }, "\n"))
2243
+ outputDir := filepath.Join(t.TempDir(), "output")
2244
+ comp, err := NewCompiler(&Config{
2245
+ Dir: moduleDir,
2246
+ OutputPath: outputDir,
2247
+ AllDependencies: true,
2248
+ OverrideDirs: []string{overrideDir},
2249
+ }, nil, nil)
2250
+ if err != nil {
2251
+ t.Fatal(err.Error())
2252
+ }
2253
+
2254
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2255
+ t.Fatal(err.Error())
2256
+ }
2257
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "override-field-type", "main.gs.ts")
2258
+ content, err := os.ReadFile(outputFile)
2259
+ if err != nil {
2260
+ t.Fatal(err.Error())
2261
+ }
2262
+ text := string(content)
2263
+ if !strings.Contains(text, "$.pointerValue<any>($.pointerValue<api.Request>(r).URL).Path") {
2264
+ t.Fatalf("missing erased override field type in generated output:\n%s", text)
2265
+ }
2266
+ if strings.Contains(text, "dep.URL") || strings.Contains(text, "pointerValue<URL>") {
2267
+ t.Fatalf("generated output referenced unavailable dependency type:\n%s", text)
2268
+ }
2269
+ }
2270
+
2271
+ func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
2272
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2273
+ "go.mod": "module example.test/iterators\n\ngo 1.25.3\n",
2274
+ "main.go": strings.Join([]string{
2275
+ "package main",
2276
+ "func pairs(yield func(int, string) bool) {",
2277
+ " values := []string{\"a\", \"b\"}",
2278
+ " for i, v := range values {",
2279
+ " if !yield(i, v) {",
2280
+ " break",
2281
+ " }",
2282
+ " }",
2283
+ "}",
2284
+ "func main() {",
2285
+ " for i, v := range pairs {",
2286
+ " println(i, v)",
2287
+ " }",
2288
+ " var last int",
2289
+ " for last = range pairs {",
2290
+ " println(last)",
2291
+ " }",
2292
+ " for i := range 3 {",
2293
+ " if i == 1 {",
2294
+ " continue",
2295
+ " }",
2296
+ " println(i)",
2297
+ " }",
2298
+ " ch := make(chan int, 1)",
2299
+ " for _, outer := range backward([]int{1, 2}) {",
2300
+ " for range backward([]int{3}) {",
2301
+ " ch <- outer",
2302
+ " }",
2303
+ " }",
2304
+ "}",
2305
+ "func backward(values []int) func(func(int, int) bool) {",
2306
+ " return func(yield func(int, int) bool) {",
2307
+ " for i := len(values) - 1; i >= 0; i-- {",
2308
+ " if !yield(i, values[i]) {",
2309
+ " return",
2310
+ " }",
2311
+ " }",
2312
+ " }",
2313
+ "}",
2314
+ "",
2315
+ }, "\n"),
2316
+ })
2317
+ outputDir := filepath.Join(t.TempDir(), "output")
2318
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2319
+ if err != nil {
2320
+ t.Fatal(err.Error())
2321
+ }
2322
+
2323
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2324
+ t.Fatal(err.Error())
2325
+ }
2326
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "iterators", "main.gs.ts")
2327
+ content, err := os.ReadFile(outputFile)
2328
+ if err != nil {
2329
+ t.Fatal(err.Error())
2330
+ }
2331
+ text := string(content)
2332
+ for _, want := range []string{
2333
+ "if (!await _yield!(i, v))",
2334
+ "break",
2335
+ "await pairs!(async (i, v) => {",
2336
+ "last = __goscriptRange",
2337
+ "return true",
2338
+ "continue",
2339
+ "await backward($.arrayToSlice<number>([1, 2]))!(async (__goscriptRange",
2340
+ "await backward($.arrayToSlice<number>([3]))!(async (__goscriptRange",
2341
+ } {
2342
+ if !strings.Contains(text, want) {
2343
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2344
+ }
2345
+ }
2346
+ }
2347
+
2348
+ func TestCompilePackagesPreservesNamedFunctionResultTypes(t *testing.T) {
2349
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2350
+ "go.mod": "module example.test/namedfuncresult\n\ngo 1.25.3\n",
2351
+ "main.go": strings.Join([]string{
2352
+ "package main",
2353
+ "type Seq func(func(int) bool)",
2354
+ "func values() Seq {",
2355
+ " return nil",
2356
+ "}",
2357
+ "func main() {",
2358
+ " if values() == nil {",
2359
+ " println(\"empty\")",
2360
+ " }",
2361
+ "}",
2362
+ "",
2363
+ }, "\n"),
2364
+ })
2365
+ outputDir := filepath.Join(t.TempDir(), "output")
2366
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2367
+ if err != nil {
2368
+ t.Fatal(err.Error())
2369
+ }
2370
+
2371
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2372
+ t.Fatal(err.Error())
2373
+ }
2374
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "namedfuncresult", "main.gs.ts")
2375
+ content, err := os.ReadFile(outputFile)
2376
+ if err != nil {
2377
+ t.Fatal(err.Error())
2378
+ }
2379
+ text := string(content)
2380
+ for _, want := range []string{
2381
+ "export type Seq = ((_p0: ((_p0: number) => boolean | globalThis.Promise<boolean>) | null) => void) | null",
2382
+ "export function values(): Seq | null",
2383
+ "return (null as Seq | null)",
2384
+ } {
2385
+ if !strings.Contains(text, want) {
2386
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2387
+ }
2388
+ }
2389
+ }
2390
+
2391
+ func TestCompilePackagesLowersFunctionIteratorControlFlow(t *testing.T) {
2392
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2393
+ "go.mod": "module example.test/iterator-control\n\ngo 1.25.3\n",
2394
+ "main.go": strings.Join([]string{
2395
+ "package main",
2396
+ "func values(yield func(int) bool) {",
2397
+ " for i := range 4 {",
2398
+ " if !yield(i) {",
2399
+ " return",
2400
+ " }",
2401
+ " }",
2402
+ "}",
2403
+ "func first(limit int) int {",
2404
+ " j := 3",
2405
+ " for i := 0; i < j; i, j = i+1, j-1 {",
2406
+ " println(i, j)",
2407
+ " }",
2408
+ " for v := range values {",
2409
+ " if v == 0 {",
2410
+ " continue",
2411
+ " }",
2412
+ " for i := range 2 {",
2413
+ " if i == 1 {",
2414
+ " break",
2415
+ " }",
2416
+ " }",
2417
+ " if v == limit {",
2418
+ " break",
2419
+ " }",
2420
+ " switch v {",
2421
+ " case 2:",
2422
+ " return v",
2423
+ " }",
2424
+ " switch any(v).(type) {",
2425
+ " case int:",
2426
+ " if v == 3 {",
2427
+ " return v",
2428
+ " }",
2429
+ " }",
2430
+ " }",
2431
+ " return -1",
2432
+ "}",
2433
+ "func nestedReturn(limit int) int {",
2434
+ " for v := range values {",
2435
+ " for i := range values {",
2436
+ " if i == limit {",
2437
+ " return v + i",
2438
+ " }",
2439
+ " }",
2440
+ " }",
2441
+ " return -1",
2442
+ "}",
2443
+ "func main() { println(first(3), nestedReturn(2)) }",
2444
+ "",
2445
+ }, "\n"),
2446
+ })
2447
+ outputDir := filepath.Join(t.TempDir(), "output")
2448
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2449
+ if err != nil {
2450
+ t.Fatal(err.Error())
2451
+ }
2452
+
2453
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2454
+ t.Fatal(err.Error())
2455
+ }
2456
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "iterator-control", "main.gs.ts")
2457
+ content, err := os.ReadFile(outputFile)
2458
+ if err != nil {
2459
+ t.Fatal(err.Error())
2460
+ }
2461
+ text := string(content)
2462
+ for _, want := range []string{
2463
+ "let __goscriptRangeReturn",
2464
+ "let __goscriptRangeReturnValue",
2465
+ "return true",
2466
+ "return false",
2467
+ "__goscriptRangeReturnValue",
2468
+ "if (__goscriptRangeReturn",
2469
+ "return __goscriptRangeReturnValue",
2470
+ "for (let i = 0; i < j; [i, j] = [i + 1, j - 1])",
2471
+ "for (let i = 0; i < 2; i++)",
2472
+ "switch (v)",
2473
+ "const __goscriptTypeSwitchValue",
2474
+ "switch (true)",
2475
+ "case $.typeAssert<number>(__goscriptTypeSwitchValue, { kind: $.TypeKind.Basic, name: \"int\" }).ok",
2476
+ "break",
2477
+ } {
2478
+ if !strings.Contains(text, want) {
2479
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2480
+ }
2481
+ }
2482
+ nestedReturn := regexp.MustCompile(`if \(__goscriptRangeReturn\d+\) \{\n\t+__goscriptRangeReturn\d+ = true\n\t+__goscriptRangeReturnValue\d+ = __goscriptRangeReturnValue\d+!\n\t+return false\n\t+\}`)
2483
+ if !nestedReturn.MatchString(text) {
2484
+ t.Fatalf("missing nested range return propagation:\n%s", text)
2485
+ }
2486
+ }
2487
+
2488
+ func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
2489
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2490
+ "go.mod": "module example.test/async\n\ngo 1.25.3\n",
2491
+ "main.go": strings.Join([]string{
2492
+ "package main",
2493
+ "type Processor interface { Process(v int) int }",
2494
+ "type Worker struct { ch chan int }",
2495
+ "func (w *Worker) Process(v int) int {",
2496
+ " w.ch <- v",
2497
+ " return <-w.ch",
2498
+ "}",
2499
+ "func call(p Processor) int { return p.Process(2) }",
2500
+ "func stopLoop(stop chan struct{}, done chan struct{}) {",
2501
+ " for {",
2502
+ " select {",
2503
+ " case <-stop:",
2504
+ " done <- struct{}{}",
2505
+ " return",
2506
+ " }",
2507
+ " }",
2508
+ "}",
2509
+ "func main() {",
2510
+ " ch := make(chan int, 1)",
2511
+ " defer func() { <-ch }()",
2512
+ " go func() { ch <- 1 }()",
2513
+ " select {",
2514
+ " case v := <-ch:",
2515
+ " println(v)",
2516
+ " default:",
2517
+ " println(\"default\")",
2518
+ " }",
2519
+ " _ = call(&Worker{ch: make(chan int, 1)})",
2520
+ "}",
2521
+ "",
2522
+ }, "\n"),
2523
+ })
2524
+ outputDir := filepath.Join(t.TempDir(), "output")
2525
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2526
+ if err != nil {
2527
+ t.Fatal(err.Error())
2528
+ }
2529
+
2530
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2531
+ t.Fatal(err.Error())
2532
+ }
2533
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "async", "main.gs.ts")
2534
+ content, err := os.ReadFile(outputFile)
2535
+ if err != nil {
2536
+ t.Fatal(err.Error())
2537
+ }
2538
+ text := string(content)
2539
+ for _, want := range []string{
2540
+ "Process(v: number): number | globalThis.Promise<number>",
2541
+ "public async Process(v: number): globalThis.Promise<number>",
2542
+ "let ch = $.makeChannel<number>(1, 0, \"both\")",
2543
+ "await $.chanSend($.pointerValue<Worker>(w).ch, v)",
2544
+ "return await $.chanRecv($.pointerValue<Worker>(w).ch)",
2545
+ "await using __defer = new $.AsyncDisposableStack()",
2546
+ "queueMicrotask(async () => { await ($.functionValue(async (): globalThis.Promise<void> => {",
2547
+ "$.selectStatement<any, void>([",
2548
+ "let v = __goscriptSelect1Result.value",
2549
+ "return $.selectVoidReturn()",
2550
+ "await call($.interfaceValue<Processor | null>(new Worker({ch: $.makeChannel<number>(1, 0, \"both\")}), \"*main.Worker\"))",
2551
+ } {
2552
+ if !strings.Contains(text, want) {
2553
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2554
+ }
2555
+ }
2556
+ }
2557
+
2558
+ func TestCompilePackagesMarksSelectReturningIfElseCasesUnreachable(t *testing.T) {
2559
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2560
+ "go.mod": "module example.test/select-if-else\n\ngo 1.25.3\n",
2561
+ "main.go": strings.Join([]string{
2562
+ "package main",
2563
+ "import \"context\"",
2564
+ "func finish(ctx context.Context, ch <-chan int, client bool) (int, error) {",
2565
+ " select {",
2566
+ " case <-ch:",
2567
+ " if client {",
2568
+ " return 1, nil",
2569
+ " } else {",
2570
+ " return 2, nil",
2571
+ " }",
2572
+ " case <-ctx.Done():",
2573
+ " return 3, ctx.Err()",
2574
+ " }",
2575
+ "}",
2576
+ "",
2577
+ }, "\n"),
2578
+ })
2579
+ outputDir := filepath.Join(t.TempDir(), "output")
2580
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2581
+ if err != nil {
2582
+ t.Fatal(err.Error())
2583
+ }
2584
+
2585
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2586
+ t.Fatal(err.Error())
2587
+ }
2588
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "select-if-else", "main.gs.ts")
2589
+ content, err := os.ReadFile(outputFile)
2590
+ if err != nil {
2591
+ t.Fatal(err.Error())
2592
+ }
2593
+ text := string(content)
2594
+ for _, want := range []string{
2595
+ "export async function finish(ctx: context.Context | null, ch: $.Channel<number> | null, client: boolean): globalThis.Promise<[number, $.GoError]>",
2596
+ "if (__goscriptSelect0HasReturn) {",
2597
+ "throw new Error(\"unreachable select\")",
2598
+ } {
2599
+ if !strings.Contains(text, want) {
2600
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2601
+ }
2602
+ }
2603
+ }
2604
+
2605
+ func TestCompilePackagesPropagatesImmediateFuncLitAsync(t *testing.T) {
2606
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2607
+ "go.mod": "module example.test/immediate-func-lit-async\n\ngo 1.25.3\n",
2608
+ "main.go": strings.Join([]string{
2609
+ "package main",
2610
+ "import \"sync\"",
2611
+ "type resolver struct {",
2612
+ " parent *resolver",
2613
+ " mutex sync.Mutex",
2614
+ "}",
2615
+ "func (r *resolver) lookup() (int, error) {",
2616
+ " value := func() int {",
2617
+ " r.mutex.Lock()",
2618
+ " defer r.mutex.Unlock()",
2619
+ " return 7",
2620
+ " }()",
2621
+ " if r.parent != nil {",
2622
+ " return r.parent.lookup()",
2623
+ " }",
2624
+ " return value, nil",
2625
+ "}",
2626
+ "func use(r *resolver) (int, error) {",
2627
+ " return r.lookup()",
2628
+ "}",
2629
+ "func main() {}",
2630
+ "",
2631
+ }, "\n"),
2632
+ })
2633
+ outputDir := filepath.Join(t.TempDir(), "output")
2634
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2635
+ if err != nil {
2636
+ t.Fatal(err.Error())
2637
+ }
2638
+
2639
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2640
+ t.Fatal(err.Error())
2641
+ }
2642
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "immediate-func-lit-async", "main.gs.ts")
2643
+ content, err := os.ReadFile(outputFile)
2644
+ if err != nil {
2645
+ t.Fatal(err.Error())
2646
+ }
2647
+ text := string(content)
2648
+ for _, want := range []string{
2649
+ "public async lookup(): globalThis.Promise<[number, $.GoError]>",
2650
+ "return await resolver.prototype.lookup.call($.pointerValue<resolver>(r).parent)",
2651
+ "export async function use(r: resolver | $.VarRef<resolver> | null): globalThis.Promise<[number, $.GoError]>",
2652
+ "return await resolver.prototype.lookup.call(r)",
2653
+ } {
2654
+ if !strings.Contains(text, want) {
2655
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2656
+ }
2657
+ }
2658
+ if strings.Contains(text, "const __goscriptReturn0 = resolver.prototype.lookup.call") {
2659
+ t.Fatalf("immediate func-literal async method call was not awaited:\n%s", text)
2660
+ }
2661
+ }
2662
+
2663
+ func TestCompilePackagesParenthesizesAsyncFieldReceivers(t *testing.T) {
2664
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2665
+ "go.mod": "module example.test/asyncfield\n\ngo 1.25.3\n",
2666
+ "main.go": strings.Join([]string{
2667
+ "package main",
2668
+ "type Result struct { ok bool }",
2669
+ "type Box struct { ch chan int }",
2670
+ "func (b *Box) next() Result {",
2671
+ " b.ch <- 1",
2672
+ " return Result{ok: true}",
2673
+ "}",
2674
+ "func (b *Box) OK() bool {",
2675
+ " return b.next().ok",
2676
+ "}",
2677
+ "func main() {",
2678
+ " box := &Box{ch: make(chan int, 1)}",
2679
+ " println(box.OK())",
2680
+ "}",
2681
+ "",
2682
+ }, "\n"),
2683
+ })
2684
+ outputDir := filepath.Join(t.TempDir(), "output")
2685
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2686
+ if err != nil {
2687
+ t.Fatal(err.Error())
2688
+ }
2689
+
2690
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2691
+ t.Fatal(err.Error())
2692
+ }
2693
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "asyncfield", "main.gs.ts")
2694
+ content, err := os.ReadFile(outputFile)
2695
+ if err != nil {
2696
+ t.Fatal(err.Error())
2697
+ }
2698
+ text := string(content)
2699
+ if !strings.Contains(text, "return (await Box.prototype.next.call(b)).ok") {
2700
+ t.Fatalf("async field receiver was not parenthesized:\n%s", text)
2701
+ }
2702
+ if strings.Contains(text, "return await Box.prototype.next.call(b).ok") {
2703
+ t.Fatalf("async field receiver selected the promise before await:\n%s", text)
2704
+ }
2705
+ }
2706
+
2707
+ func TestCompilePackagesScopesIfInitDeclarations(t *testing.T) {
2708
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2709
+ "go.mod": "module example.test/ifinit\n\ngo 1.25.3\n",
2710
+ "main.go": strings.Join([]string{
2711
+ "package main",
2712
+ "func pair() (string, bool) {",
2713
+ " return \"value\", true",
2714
+ "}",
2715
+ "func main() {",
2716
+ " if value, ok := pair(); ok {",
2717
+ " println(value)",
2718
+ " }",
2719
+ " if value, ok := pair(); ok {",
2720
+ " println(value)",
2721
+ " }",
2722
+ "}",
2723
+ "",
2724
+ }, "\n"),
2725
+ })
2726
+ outputDir := filepath.Join(t.TempDir(), "output")
2727
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2728
+ if err != nil {
2729
+ t.Fatal(err.Error())
2730
+ }
2731
+
2732
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2733
+ t.Fatal(err.Error())
2734
+ }
2735
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "ifinit", "main.gs.ts"))
2736
+ if err != nil {
2737
+ t.Fatal(err.Error())
2738
+ }
2739
+ if strings.Count(string(content), "{\n\t\tlet [value, ok] = pair()") != 2 {
2740
+ t.Fatalf("if init declarations were not block scoped:\n%s", string(content))
2741
+ }
2742
+ }
2743
+
2744
+ func TestCompilePackagesLowersSwitchesAndFunctionValueCalls(t *testing.T) {
2745
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2746
+ "go.mod": "module example.test/switchcall\n\ngo 1.25.3\n",
2747
+ "main.go": strings.Join([]string{
2748
+ "package main",
2749
+ "func main() {",
2750
+ " value := 2",
2751
+ " switch value {",
2752
+ " case 1:",
2753
+ " println(\"one\")",
2754
+ " case 2, 3:",
2755
+ " local := \"two-three\"",
2756
+ " println(local)",
2757
+ " default:",
2758
+ " println(\"other\")",
2759
+ " }",
2760
+ " switch {",
2761
+ " case value > 1:",
2762
+ " println(\"positive\")",
2763
+ " }",
2764
+ "Block:",
2765
+ " for value > 0 {",
2766
+ " switch value {",
2767
+ " case 2:",
2768
+ " value--",
2769
+ " fallthrough",
2770
+ " case 1:",
2771
+ " break Block",
2772
+ " }",
2773
+ " }",
2774
+ "Again:",
2775
+ " value--",
2776
+ " if value > 0 {",
2777
+ " goto Again",
2778
+ " }",
2779
+ " release := func() { println(\"release\") }",
2780
+ " rel := &release",
2781
+ " (*rel)()",
2782
+ " wrapped := func() {",
2783
+ " defer println(\"wrapped deferred\")",
2784
+ " println(\"wrapped body\")",
2785
+ " }",
2786
+ " wrapped()",
2787
+ "}",
2788
+ "",
2789
+ }, "\n"),
2790
+ })
2791
+ outputDir := filepath.Join(t.TempDir(), "output")
2792
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2793
+ if err != nil {
2794
+ t.Fatal(err.Error())
2795
+ }
2796
+
2797
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2798
+ t.Fatal(err.Error())
2799
+ }
2800
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "switchcall", "main.gs.ts"))
2801
+ if err != nil {
2802
+ t.Fatal(err.Error())
2803
+ }
2804
+ text := string(content)
2805
+ for _, want := range []string{
2806
+ "switch (value) {",
2807
+ "case 2:",
2808
+ "case 3:",
2809
+ "let local = \"two-three\"",
2810
+ "switch (true) {",
2811
+ "Block: while (value > 0)",
2812
+ "break Block",
2813
+ "Again: while (true)",
2814
+ "continue Again",
2815
+ "($.pointerValue<(() => void) | null>(rel))!()",
2816
+ "$.functionValue((): void => {\n\t\tusing __defer = new $.DisposableStack()",
2817
+ "__defer.defer(() => { $.println(\"wrapped deferred\") })",
2818
+ "$.println(\"wrapped body\")",
2819
+ } {
2820
+ if !strings.Contains(text, want) {
2821
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2822
+ }
2823
+ }
2824
+ if strings.Count(text, "\t\tbreak\n") < 3 {
2825
+ t.Fatalf("switch cases were not rendered with implicit breaks:\n%s", text)
2826
+ }
2827
+ if strings.Contains(text, "fallthrough") {
2828
+ t.Fatalf("fallthrough marker leaked into generated output:\n%s", text)
2829
+ }
2830
+ }
2831
+
2832
+ func TestCompilePackagesLowersMethodValuesWithFixedParameters(t *testing.T) {
2833
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2834
+ "go.mod": "module example.test/methodvalue\n\ngo 1.25.3\n",
2835
+ "main.go": strings.Join([]string{
2836
+ "package main",
2837
+ "type Counter int",
2838
+ "func (c Counter) Add(n int) int {",
2839
+ " return int(c) + n",
2840
+ "}",
2841
+ "type Runner struct{}",
2842
+ "func (r Runner) Run() {",
2843
+ " println(\"run\")",
2844
+ "}",
2845
+ "func main() {",
2846
+ " c := Counter(4)",
2847
+ " add := c.Add",
2848
+ " println(add(3))",
2849
+ " r := Runner{}",
2850
+ " run := r.Run",
2851
+ " run()",
2852
+ "}",
2853
+ "",
2854
+ }, "\n"),
2855
+ })
2856
+ outputDir := filepath.Join(t.TempDir(), "output")
2857
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2858
+ if err != nil {
2859
+ t.Fatal(err.Error())
2860
+ }
2861
+
2862
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2863
+ t.Fatal(err.Error())
2864
+ }
2865
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "methodvalue", "main.gs.ts"))
2866
+ if err != nil {
2867
+ t.Fatal(err.Error())
2868
+ }
2869
+ text := string(content)
2870
+ for _, want := range []string{
2871
+ "((__receiver) => (n: number) => Counter_Add(__receiver, n))(c)",
2872
+ "((__receiver) => () => __receiver.Run())(",
2873
+ } {
2874
+ if !strings.Contains(text, want) {
2875
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2876
+ }
2877
+ }
2878
+ if strings.Contains(text, "...args: any[]") {
2879
+ t.Fatalf("method value lowering still uses spread args:\n%s", text)
2880
+ }
2881
+ }
2882
+
2883
+ func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testing.T) {
2884
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2885
+ "go.mod": "module example.test/qualified\n\ngo 1.25.3\n",
2886
+ "lib/lib.go": strings.Join([]string{
1035
2887
  "package lib",
1036
2888
  "type Box struct {",
1037
2889
  " Value int",
@@ -1077,11 +2929,11 @@ func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testi
1077
2929
  for _, want := range []string{
1078
2930
  "Box: $.VarRef<lib.Box>",
1079
2931
  "Boxes: $.VarRef<$.Slice<lib.Box>>",
1080
- "Fn: $.VarRef<((_p0: lib.Box) => [lib.Box, $.GoError]) | null>",
2932
+ "Fn: $.VarRef<((_p0: lib.Box) => [lib.Box, $.GoError] | globalThis.Promise<[lib.Box, $.GoError]>) | null>",
1081
2933
  "Ptr: $.VarRef<atomic.Pointer<(() => void) | null>>",
1082
2934
  "$.markAsStructValue(new lib.Box())",
1083
2935
  "$.markAsStructValue(new atomic.Pointer<(() => void) | null>())",
1084
- "export function Use(fn: ((_p0: lib.Box) => [lib.Box, $.GoError]) | null, box: lib.Box): [lib.Box, $.GoError]",
2936
+ "export async function Use(fn: ((_p0: lib.Box) => [lib.Box, $.GoError] | globalThis.Promise<[lib.Box, $.GoError]>) | null, box: lib.Box): globalThis.Promise<[lib.Box, $.GoError]>",
1085
2937
  "$.functionValue((box: lib.Box): [lib.Box, $.GoError] => {",
1086
2938
  } {
1087
2939
  if !strings.Contains(text, want) {
@@ -1090,14 +2942,16 @@ func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testi
1090
2942
  }
1091
2943
  }
1092
2944
 
1093
- func TestCompilePackagesReportsUnsupportedUnaryBeforeOutput(t *testing.T) {
2945
+ func TestCompilePackagesLowersUnaryBitwiseComplement(t *testing.T) {
1094
2946
  moduleDir := writePackageGraphFixture(t, map[string]string{
1095
- "go.mod": "module example.test/unsupported\n\ngo 1.25.3\n",
2947
+ "go.mod": "module example.test/unary-bitwise\n\ngo 1.25.3\n",
1096
2948
  "main.go": strings.Join([]string{
1097
2949
  "package main",
1098
2950
  "var value = 1",
1099
2951
  "func main() {",
1100
- " println(^value)",
2952
+ " mask := 7",
2953
+ " mask &^= 3",
2954
+ " println(^value, value &^ 3, mask, 0700)",
1101
2955
  "}",
1102
2956
  "",
1103
2957
  }, "\n"),
@@ -1108,25 +2962,424 @@ func TestCompilePackagesReportsUnsupportedUnaryBeforeOutput(t *testing.T) {
1108
2962
  t.Fatal(err.Error())
1109
2963
  }
1110
2964
 
1111
- result, err := comp.CompilePackages(context.Background(), ".")
1112
- if err == nil {
1113
- t.Fatal("expected unsupported unary expression to fail")
2965
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2966
+ t.Fatal(err.Error())
1114
2967
  }
1115
- requireDiagnostic(t, err, "goscript/lowering:unsupported")
1116
- if result == nil || len(result.Diagnostics) == 0 {
1117
- t.Fatalf("expected structured diagnostics in result")
2968
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "unary-bitwise", "main.gs.ts")
2969
+ content, err := os.ReadFile(outputFile)
2970
+ if err != nil {
2971
+ t.Fatal(err.Error())
1118
2972
  }
1119
- if _, statErr := os.Stat(outputDir); !os.IsNotExist(statErr) {
1120
- t.Fatalf("compile wrote output directory after lowering failed: %v", statErr)
2973
+ text := string(content)
2974
+ for _, want := range []string{
2975
+ "mask = mask & ~(3)",
2976
+ "$.println(~value, value & ~(3), mask, 0o700)",
2977
+ } {
2978
+ if !strings.Contains(text, want) {
2979
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2980
+ }
1121
2981
  }
1122
2982
  }
1123
2983
 
1124
- func TestCompileSourceToTypeScriptStopsAtSkeleton(t *testing.T) {
1125
- _, err := CompileSourceToTypeScript("package main\nfunc main() {}\n", "main")
1126
- if err == nil {
1127
- t.Fatal("expected WASM source compile to fail in the skeleton")
2984
+ func TestCompilePackagesParenthesizesRepeatedUnarySigns(t *testing.T) {
2985
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2986
+ "go.mod": "module example.test/unary-signs\n\ngo 1.25.3\n",
2987
+ "constants.go": strings.Join([]string{
2988
+ "package main",
2989
+ "const extOffset = -0x1000",
2990
+ "",
2991
+ }, "\n"),
2992
+ "main.go": strings.Join([]string{
2993
+ "package main",
2994
+ "type Extension int32",
2995
+ "type LoadExtension struct { Num Extension }",
2996
+ "func Decode(k int32) LoadExtension {",
2997
+ " return LoadExtension{Num: Extension(-extOffset + k)}",
2998
+ "}",
2999
+ "",
3000
+ }, "\n"),
3001
+ })
3002
+ outputDir := filepath.Join(t.TempDir(), "output")
3003
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3004
+ if err != nil {
3005
+ t.Fatal(err.Error())
3006
+ }
3007
+
3008
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3009
+ t.Fatal(err.Error())
3010
+ }
3011
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "unary-signs", "main.gs.ts")
3012
+ content, err := os.ReadFile(outputFile)
3013
+ if err != nil {
3014
+ t.Fatal(err.Error())
3015
+ }
3016
+ text := string(content)
3017
+ if strings.Contains(text, "--4096") {
3018
+ t.Fatalf("generated invalid decrement token:\n%s", text)
3019
+ }
3020
+ if !strings.Contains(text, "-(-4096) + k") {
3021
+ t.Fatalf("missing parenthesized negative constant:\n%s", text)
3022
+ }
3023
+ }
3024
+
3025
+ func TestCompilePackagesNormalizesWideIntegerReturnTargets(t *testing.T) {
3026
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3027
+ "go.mod": "module example.test/wide-return\n\ngo 1.25.3\n",
3028
+ "main.go": strings.Join([]string{
3029
+ "package main",
3030
+ "import \"hash\"",
3031
+ "func Read(h hash.Hash64) uint64 {",
3032
+ " return h.Sum64()",
3033
+ "}",
3034
+ "",
3035
+ }, "\n"),
3036
+ })
3037
+ outputDir := filepath.Join(t.TempDir(), "output")
3038
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3039
+ if err != nil {
3040
+ t.Fatal(err.Error())
3041
+ }
3042
+
3043
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3044
+ t.Fatal(err.Error())
3045
+ }
3046
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "wide-return", "main.gs.ts")
3047
+ content, err := os.ReadFile(outputFile)
3048
+ if err != nil {
3049
+ t.Fatal(err.Error())
3050
+ }
3051
+ text := string(content)
3052
+ if !strings.Contains(text, "return $.uint($.pointerValue<Exclude<hash.Hash64, null>>(h).Sum64(), 64)") {
3053
+ t.Fatalf("missing uint64 return normalization:\n%s", text)
3054
+ }
3055
+ }
3056
+
3057
+ func TestCompilePackagesUnwrapsImportedVarRefValueMethodReceiver(t *testing.T) {
3058
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3059
+ "go.mod": "module example.test/imported-varref-receiver\n\ngo 1.25.3\n",
3060
+ "dep/dep.go": strings.Join([]string{
3061
+ "package dep",
3062
+ "type Info struct { Count int }",
3063
+ "func (i Info) Enabled() bool { return i.Count > 0 }",
3064
+ "func addInfo(i *Info) { i.Count = 1 }",
3065
+ "var CPU Info",
3066
+ "func init() { addInfo(&CPU) }",
3067
+ "",
3068
+ }, "\n"),
3069
+ "main.go": strings.Join([]string{
3070
+ "package main",
3071
+ "import \"example.test/imported-varref-receiver/dep\"",
3072
+ "func Enabled() bool {",
3073
+ " return dep.CPU.Enabled()",
3074
+ "}",
3075
+ "",
3076
+ }, "\n"),
3077
+ })
3078
+ outputDir := filepath.Join(t.TempDir(), "output")
3079
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3080
+ if err != nil {
3081
+ t.Fatal(err.Error())
3082
+ }
3083
+
3084
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3085
+ t.Fatal(err.Error())
3086
+ }
3087
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "imported-varref-receiver", "main.gs.ts")
3088
+ content, err := os.ReadFile(outputFile)
3089
+ if err != nil {
3090
+ t.Fatal(err.Error())
3091
+ }
3092
+ text := string(content)
3093
+ if !strings.Contains(text, "$.cloneStructValue($.pointerValue<dep.Info>(dep.CPU))") {
3094
+ t.Fatalf("missing imported VarRef receiver unwrap:\n%s", text)
3095
+ }
3096
+ if strings.Contains(text, "$.cloneStructValue(dep.CPU))") {
3097
+ t.Fatalf("imported VarRef receiver stayed wrapped:\n%s", text)
3098
+ }
3099
+ }
3100
+
3101
+ func TestCompilePackagesUnwrapsImportedArrayPackageVarReads(t *testing.T) {
3102
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3103
+ "go.mod": "module example.test/imported-array-var\n\ngo 1.25.3\n",
3104
+ "dep/dep.go": strings.Join([]string{
3105
+ "package dep",
3106
+ "var Table = [2]int{3, 5}",
3107
+ "func touch(v *[2]int) { v[0]++ }",
3108
+ "func init() { touch(&Table) }",
3109
+ "func Sum(v [2]int) int { return v[0] + v[1] }",
3110
+ "",
3111
+ }, "\n"),
3112
+ "main.go": strings.Join([]string{
3113
+ "package main",
3114
+ "import \"example.test/imported-array-var/dep\"",
3115
+ "func Read() int {",
3116
+ " return dep.Table[1] + dep.Sum(dep.Table)",
3117
+ "}",
3118
+ "",
3119
+ }, "\n"),
3120
+ })
3121
+ outputDir := filepath.Join(t.TempDir(), "output")
3122
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3123
+ if err != nil {
3124
+ t.Fatal(err.Error())
3125
+ }
3126
+
3127
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3128
+ t.Fatal(err.Error())
3129
+ }
3130
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "imported-array-var", "main.gs.ts")
3131
+ content, err := os.ReadFile(outputFile)
3132
+ if err != nil {
3133
+ t.Fatal(err.Error())
3134
+ }
3135
+ text := string(content)
3136
+ for _, want := range []string{
3137
+ "$.pointerValue<number[]>(dep.Table)[1]",
3138
+ "dep.Sum($.pointerValue<number[]>(dep.Table))",
3139
+ } {
3140
+ if !strings.Contains(text, want) {
3141
+ t.Fatalf("missing imported array package var read %q:\n%s", want, text)
3142
+ }
3143
+ }
3144
+ }
3145
+
3146
+ func TestCompilePackagesAddressesImportedArrayPackageVarsAsRefs(t *testing.T) {
3147
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3148
+ "go.mod": "module example.test/imported-array-var-address\n\ngo 1.25.3\n",
3149
+ "dep/dep.go": strings.Join([]string{
3150
+ "package dep",
3151
+ "var Table = [2]int{3, 5}",
3152
+ "func touch(v *[2]int) { v[0]++ }",
3153
+ "func init() { touch(&Table) }",
3154
+ "func SumPtr(v *[2]int) int { return v[0] + v[1] }",
3155
+ "",
3156
+ }, "\n"),
3157
+ "main.go": strings.Join([]string{
3158
+ "package main",
3159
+ "import \"example.test/imported-array-var-address/dep\"",
3160
+ "func Read() int {",
3161
+ " return dep.SumPtr(&dep.Table)",
3162
+ "}",
3163
+ "",
3164
+ }, "\n"),
3165
+ })
3166
+ outputDir := filepath.Join(t.TempDir(), "output")
3167
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3168
+ if err != nil {
3169
+ t.Fatal(err.Error())
3170
+ }
3171
+
3172
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3173
+ t.Fatal(err.Error())
3174
+ }
3175
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "imported-array-var-address", "main.gs.ts")
3176
+ content, err := os.ReadFile(outputFile)
3177
+ if err != nil {
3178
+ t.Fatal(err.Error())
3179
+ }
3180
+ text := string(content)
3181
+ if !strings.Contains(text, "dep.SumPtr(dep.Table)") {
3182
+ t.Fatalf("missing imported array package var address:\n%s", text)
3183
+ }
3184
+ if strings.Contains(text, "dep.SumPtr($.pointerValue<number[]>(dep.Table))") ||
3185
+ strings.Contains(text, "dep._fields.Table") {
3186
+ t.Fatalf("imported array package var address was lowered as a read or field:\n%s", text)
3187
+ }
3188
+ }
3189
+
3190
+ func TestCompilePackagesUnwrapsAliasedArrayPackageVarReads(t *testing.T) {
3191
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3192
+ "go.mod": "module example.test/aliased-array-var\n\ngo 1.25.3\n",
3193
+ "table.go": strings.Join([]string{
3194
+ "package main",
3195
+ "var Table = [2]int{3, 5}",
3196
+ "func touch(v *[2]int) { v[0]++ }",
3197
+ "func init() { touch(&Table) }",
3198
+ "func Sum(v [2]int) int { return v[0] + v[1] }",
3199
+ "",
3200
+ }, "\n"),
3201
+ "read.go": strings.Join([]string{
3202
+ "package main",
3203
+ "func Read() int {",
3204
+ " return Table[1] + Sum(Table)",
3205
+ "}",
3206
+ "",
3207
+ }, "\n"),
3208
+ })
3209
+ outputDir := filepath.Join(t.TempDir(), "output")
3210
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3211
+ if err != nil {
3212
+ t.Fatal(err.Error())
3213
+ }
3214
+
3215
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3216
+ t.Fatal(err.Error())
3217
+ }
3218
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "aliased-array-var", "read.gs.ts")
3219
+ content, err := os.ReadFile(outputFile)
3220
+ if err != nil {
3221
+ t.Fatal(err.Error())
3222
+ }
3223
+ text := string(content)
3224
+ for _, want := range []string{
3225
+ "$.pointerValue<number[]>(__goscript_table.Table)[1]",
3226
+ "Sum($.pointerValue<number[]>(__goscript_table.Table))",
3227
+ } {
3228
+ if !strings.Contains(text, want) {
3229
+ t.Fatalf("missing aliased array package var read %q:\n%s", want, text)
3230
+ }
3231
+ }
3232
+ }
3233
+
3234
+ func TestCompilePackagesAddressesAliasedArrayPackageVarsAsRefs(t *testing.T) {
3235
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3236
+ "go.mod": "module example.test/aliased-array-var-address\n\ngo 1.25.3\n",
3237
+ "table.go": strings.Join([]string{
3238
+ "package main",
3239
+ "var Table = [2]int{3, 5}",
3240
+ "func touch(v *[2]int) { v[0]++ }",
3241
+ "func init() { touch(&Table) }",
3242
+ "func SumPtr(v *[2]int) int { return v[0] + v[1] }",
3243
+ "",
3244
+ }, "\n"),
3245
+ "read.go": strings.Join([]string{
3246
+ "package main",
3247
+ "func Read() int {",
3248
+ " return SumPtr(&Table)",
3249
+ "}",
3250
+ "",
3251
+ }, "\n"),
3252
+ })
3253
+ outputDir := filepath.Join(t.TempDir(), "output")
3254
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3255
+ if err != nil {
3256
+ t.Fatal(err.Error())
3257
+ }
3258
+
3259
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3260
+ t.Fatal(err.Error())
3261
+ }
3262
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "aliased-array-var-address", "read.gs.ts")
3263
+ content, err := os.ReadFile(outputFile)
3264
+ if err != nil {
3265
+ t.Fatal(err.Error())
3266
+ }
3267
+ text := string(content)
3268
+ if !strings.Contains(text, "SumPtr(__goscript_table.Table)") {
3269
+ t.Fatalf("missing aliased array package var address:\n%s", text)
3270
+ }
3271
+ if strings.Contains(text, "SumPtr($.pointerValue<number[]>(__goscript_table.Table))") {
3272
+ t.Fatalf("aliased array package var address was lowered as a read:\n%s", text)
3273
+ }
3274
+ }
3275
+
3276
+ func TestCompileSourceToTypeScriptCompilesSingleFile(t *testing.T) {
3277
+ output, err := CompileSourceToTypeScript("package main\nfunc main() { println(\"hi\") }\n", "main")
3278
+ if err != nil {
3279
+ t.Fatal(err.Error())
3280
+ }
3281
+ if !strings.Contains(output, "$.println(\"hi\")") {
3282
+ t.Fatalf("missing println in generated output:\n%s", output)
3283
+ }
3284
+ }
3285
+
3286
+ func TestTypeScriptEmitOwnerEmitsToMemoryOnDiskPath(t *testing.T) {
3287
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3288
+ "go.mod": "module example.test/memoryemit\n\ngo 1.25.3\n",
3289
+ "main.go": "package main\nfunc main() { println(\"memory\") }\n",
3290
+ })
3291
+ outputDir := filepath.Join(t.TempDir(), "output")
3292
+ req := &CompileRequest{
3293
+ Patterns: []string{"."},
3294
+ Dir: moduleDir,
3295
+ OutputPath: outputDir,
3296
+ DependencyMode: DependencyModeRequested,
3297
+ RuntimeEmissionMode: RuntimeEmissionModeReference,
3298
+ }
3299
+ service := NewCompileService()
3300
+ graph, diagnostics := service.PackageGraphOwner().Load(context.Background(), req)
3301
+ if diagnosticsHaveErrors(diagnostics) {
3302
+ t.Fatalf("graph diagnostics: %#v", diagnostics)
3303
+ }
3304
+ model, diagnostics := service.SemanticModelOwner().Build(context.Background(), graph)
3305
+ if diagnosticsHaveErrors(diagnostics) {
3306
+ t.Fatalf("semantic diagnostics: %#v", diagnostics)
3307
+ }
3308
+ program, diagnostics := service.LoweringOwner().Build(context.Background(), model)
3309
+ if diagnosticsHaveErrors(diagnostics) {
3310
+ t.Fatalf("lowering diagnostics: %#v", diagnostics)
3311
+ }
3312
+
3313
+ files, diagnostics := service.TypeScriptEmitOwner().EmitToMemory(context.Background(), program)
3314
+ if diagnosticsHaveErrors(diagnostics) {
3315
+ t.Fatalf("memory emit diagnostics: %#v", diagnostics)
3316
+ }
3317
+ path := "@goscript/example.test/memoryemit/main.gs.ts"
3318
+ if !strings.Contains(files[path], "$.println(\"memory\")") {
3319
+ t.Fatalf("missing in-memory output: %#v", files)
3320
+ }
3321
+ if _, diagnostics := service.TypeScriptEmitOwner().Emit(context.Background(), req, program); diagnosticsHaveErrors(diagnostics) {
3322
+ t.Fatalf("disk emit diagnostics: %#v", diagnostics)
3323
+ }
3324
+ content, err := os.ReadFile(filepath.Join(outputDir, filepath.FromSlash(path)))
3325
+ if err != nil {
3326
+ t.Fatal(err.Error())
3327
+ }
3328
+ if string(content) != files[path] {
3329
+ t.Fatalf("disk and memory emit diverged:\n%s\n---\n%s", string(content), files[path])
3330
+ }
3331
+ }
3332
+
3333
+ func TestCompilePackagesLowersNamedStructConversionWithTypedAsyncFact(t *testing.T) {
3334
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3335
+ "go.mod": "module example.test/namedstructconvert\n\ngo 1.25.3\n",
3336
+ "main.go": strings.Join([]string{
3337
+ "package main",
3338
+ "type Source struct { Value int }",
3339
+ "type Target Source",
3340
+ "func Make() Source {",
3341
+ " ch := make(chan Source, 1)",
3342
+ " ch <- Source{Value: 7}",
3343
+ " return <-ch",
3344
+ "}",
3345
+ "func Convert() Target {",
3346
+ " return Target(Make())",
3347
+ "}",
3348
+ "func ConvertLiteral() Target {",
3349
+ " return Target(func() Source {",
3350
+ " ch := make(chan Source, 1)",
3351
+ " ch <- Source{Value: 9}",
3352
+ " return <-ch",
3353
+ " }())",
3354
+ "}",
3355
+ "func main() { println(Convert().Value) }",
3356
+ "",
3357
+ }, "\n"),
3358
+ })
3359
+ outputDir := filepath.Join(t.TempDir(), "output")
3360
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3361
+ if err != nil {
3362
+ t.Fatal(err.Error())
3363
+ }
3364
+
3365
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3366
+ t.Fatal(err.Error())
3367
+ }
3368
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "namedstructconvert", "main.gs.ts")
3369
+ content, err := os.ReadFile(outputFile)
3370
+ if err != nil {
3371
+ t.Fatal(err.Error())
3372
+ }
3373
+ text := string(content)
3374
+ if !strings.Contains(text, "await (async () => { const __goscriptConvert") {
3375
+ t.Fatalf("missing async named struct conversion:\n%s", text)
3376
+ }
3377
+ if !strings.Contains(text, "$.markAsStructValue(new Target({Value: __goscriptConvert") {
3378
+ t.Fatalf("missing typed named struct conversion target:\n%s", text)
3379
+ }
3380
+ if !strings.Contains(text, "const __goscriptConvert1 = await ($.functionValue(async ") {
3381
+ t.Fatalf("missing async fact from function literal conversion source:\n%s", text)
1128
3382
  }
1129
- requireDiagnostic(t, err, "goscript/wasm:single-file-unsupported")
1130
3383
  }
1131
3384
 
1132
3385
  func requireDiagnostic(t *testing.T, err error, code string) {