goscript 0.1.1 → 0.1.3

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 (376) hide show
  1. package/cmd/goscript/cmd-test.go +104 -11
  2. package/cmd/goscript/cmd-test_test.go +1 -1
  3. package/cmd/goscript/cmd_compile.go +9 -0
  4. package/compiler/compile-request.go +31 -0
  5. package/compiler/compiler.go +1 -1
  6. package/compiler/compliance_test.go +0 -2
  7. package/compiler/config.go +2 -0
  8. package/compiler/gotest/package-result.go +2 -0
  9. package/compiler/gotest/request.go +85 -20
  10. package/compiler/gotest/runner.go +733 -96
  11. package/compiler/gotest/runner_test.go +647 -3
  12. package/compiler/lowered-program.go +10 -2
  13. package/compiler/lowering.go +2676 -349
  14. package/compiler/override-facts.go +77 -27
  15. package/compiler/override-registry.go +5 -4
  16. package/compiler/override-registry_test.go +178 -0
  17. package/compiler/package-graph_test.go +62 -7
  18. package/compiler/package-test-graph-variant.go +40 -16
  19. package/compiler/package-test-graph.go +0 -5
  20. package/compiler/package-test-graph_test.go +61 -3
  21. package/compiler/runtime-contract.go +40 -0
  22. package/compiler/semantic-model-types.go +16 -0
  23. package/compiler/semantic-model.go +336 -91
  24. package/compiler/semantic-model_test.go +50 -1
  25. package/compiler/service.go +9 -3
  26. package/compiler/skeleton_test.go +2371 -296
  27. package/compiler/tsworkspace/owner-process-unix_test.go +72 -0
  28. package/compiler/tsworkspace/owner.go +8 -0
  29. package/compiler/tsworkspace/tool-process-other.go +14 -0
  30. package/compiler/tsworkspace/tool-process-unix.go +19 -0
  31. package/compiler/typescript-emitter.go +149 -10
  32. package/dist/gs/builtin/builtin.d.ts +20 -1
  33. package/dist/gs/builtin/builtin.js +246 -26
  34. package/dist/gs/builtin/builtin.js.map +1 -1
  35. package/dist/gs/builtin/channel.d.ts +24 -10
  36. package/dist/gs/builtin/channel.js +143 -34
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/defer.d.ts +1 -0
  39. package/dist/gs/builtin/defer.js +12 -2
  40. package/dist/gs/builtin/defer.js.map +1 -1
  41. package/dist/gs/builtin/hostio.d.ts +9 -0
  42. package/dist/gs/builtin/hostio.js +25 -0
  43. package/dist/gs/builtin/hostio.js.map +1 -1
  44. package/dist/gs/builtin/map.js +40 -6
  45. package/dist/gs/builtin/map.js.map +1 -1
  46. package/dist/gs/builtin/print.js.map +1 -1
  47. package/dist/gs/builtin/slice.d.ts +43 -9
  48. package/dist/gs/builtin/slice.js +437 -234
  49. package/dist/gs/builtin/slice.js.map +1 -1
  50. package/dist/gs/builtin/type.d.ts +2 -0
  51. package/dist/gs/builtin/type.js +55 -10
  52. package/dist/gs/builtin/type.js.map +1 -1
  53. package/dist/gs/builtin/varRef.d.ts +2 -0
  54. package/dist/gs/builtin/varRef.js.map +1 -1
  55. package/dist/gs/bytes/buffer.gs.js +28 -28
  56. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  57. package/dist/gs/bytes/bytes.gs.d.ts +7 -5
  58. package/dist/gs/bytes/bytes.gs.js +10 -4
  59. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  60. package/dist/gs/bytes/iter.gs.js +13 -13
  61. package/dist/gs/bytes/iter.gs.js.map +1 -1
  62. package/dist/gs/compress/zlib/index.d.ts +26 -0
  63. package/dist/gs/compress/zlib/index.js +168 -0
  64. package/dist/gs/compress/zlib/index.js.map +1 -0
  65. package/dist/gs/context/context.d.ts +1 -1
  66. package/dist/gs/context/context.js +8 -3
  67. package/dist/gs/context/context.js.map +1 -1
  68. package/dist/gs/crypto/ecdh/index.d.ts +52 -0
  69. package/dist/gs/crypto/ecdh/index.js +226 -0
  70. package/dist/gs/crypto/ecdh/index.js.map +1 -0
  71. package/dist/gs/crypto/ed25519/index.d.ts +34 -0
  72. package/dist/gs/crypto/ed25519/index.js +160 -0
  73. package/dist/gs/crypto/ed25519/index.js.map +1 -0
  74. package/dist/gs/crypto/internal/constanttime/index.d.ts +4 -0
  75. package/dist/gs/crypto/internal/constanttime/index.js +18 -0
  76. package/dist/gs/crypto/internal/constanttime/index.js.map +1 -0
  77. package/dist/gs/crypto/rand/index.d.ts +2 -0
  78. package/dist/gs/crypto/rand/index.js +85 -0
  79. package/dist/gs/crypto/rand/index.js.map +1 -1
  80. package/dist/gs/crypto/sha1/index.d.ts +5 -0
  81. package/dist/gs/crypto/sha1/index.js +106 -0
  82. package/dist/gs/crypto/sha1/index.js.map +1 -0
  83. package/dist/gs/crypto/sha256/index.d.ts +8 -0
  84. package/dist/gs/crypto/sha256/index.js +118 -0
  85. package/dist/gs/crypto/sha256/index.js.map +1 -0
  86. package/dist/gs/crypto/sha512/index.d.ts +14 -0
  87. package/dist/gs/crypto/sha512/index.js +129 -0
  88. package/dist/gs/crypto/sha512/index.js.map +1 -0
  89. package/dist/gs/encoding/json/index.d.ts +3 -0
  90. package/dist/gs/encoding/json/index.js +15 -0
  91. package/dist/gs/encoding/json/index.js.map +1 -1
  92. package/dist/gs/errors/errors.js +29 -6
  93. package/dist/gs/errors/errors.js.map +1 -1
  94. package/dist/gs/fmt/fmt.d.ts +1 -1
  95. package/dist/gs/fmt/fmt.js +64 -3
  96. package/dist/gs/fmt/fmt.js.map +1 -1
  97. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +7 -7
  98. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +52 -18
  99. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  100. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +56 -20
  101. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
  102. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +57 -3
  103. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +366 -1
  104. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -1
  105. package/dist/gs/github.com/aperturerobotics/util/conc/index.d.ts +20 -0
  106. package/dist/gs/github.com/aperturerobotics/util/conc/index.js +134 -0
  107. package/dist/gs/github.com/aperturerobotics/util/conc/index.js.map +1 -0
  108. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  109. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.d.ts +3 -0
  110. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.js +50 -0
  111. package/dist/gs/github.com/hack-pad/safejs/internal/catch/index.js.map +1 -0
  112. package/dist/gs/github.com/klauspost/compress/internal/le/index.js +3 -2
  113. package/dist/gs/github.com/klauspost/compress/internal/le/index.js.map +1 -1
  114. package/dist/gs/github.com/mr-tron/base58/base58/index.d.ts +27 -0
  115. package/dist/gs/github.com/mr-tron/base58/base58/index.js +172 -0
  116. package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -0
  117. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.d.ts +21 -0
  118. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.js +22 -0
  119. package/dist/gs/github.com/zeebo/blake3/internal/consts/index.js.map +1 -0
  120. package/dist/gs/go/token/index.js +11 -4
  121. package/dist/gs/go/token/index.js.map +1 -1
  122. package/dist/gs/hash/fnv/index.d.ts +57 -0
  123. package/dist/gs/hash/fnv/index.js +299 -0
  124. package/dist/gs/hash/fnv/index.js.map +1 -0
  125. package/dist/gs/hash/index.d.ts +17 -0
  126. package/dist/gs/hash/index.js +94 -0
  127. package/dist/gs/hash/index.js.map +1 -0
  128. package/dist/gs/io/fs/readlink.js +2 -6
  129. package/dist/gs/io/fs/readlink.js.map +1 -1
  130. package/dist/gs/io/fs/walk.js.map +1 -1
  131. package/dist/gs/io/io.d.ts +8 -5
  132. package/dist/gs/io/io.js +20 -2
  133. package/dist/gs/io/io.js.map +1 -1
  134. package/dist/gs/iter/iter.d.ts +3 -2
  135. package/dist/gs/iter/iter.js.map +1 -1
  136. package/dist/gs/maps/iter.d.ts +5 -5
  137. package/dist/gs/maps/iter.js +48 -21
  138. package/dist/gs/maps/iter.js.map +1 -1
  139. package/dist/gs/maps/maps.d.ts +6 -6
  140. package/dist/gs/math/bits/index.js +14 -24
  141. package/dist/gs/math/bits/index.js.map +1 -1
  142. package/dist/gs/mime/index.js +3 -1
  143. package/dist/gs/mime/index.js.map +1 -1
  144. package/dist/gs/net/http/httptest/index.d.ts +20 -1
  145. package/dist/gs/net/http/httptest/index.js +85 -3
  146. package/dist/gs/net/http/httptest/index.js.map +1 -1
  147. package/dist/gs/net/http/index.d.ts +118 -6
  148. package/dist/gs/net/http/index.js +389 -14
  149. package/dist/gs/net/http/index.js.map +1 -1
  150. package/dist/gs/net/http/pprof/index.d.ts +8 -0
  151. package/dist/gs/net/http/pprof/index.js +59 -0
  152. package/dist/gs/net/http/pprof/index.js.map +1 -0
  153. package/dist/gs/os/error.gs.js +9 -7
  154. package/dist/gs/os/error.gs.js.map +1 -1
  155. package/dist/gs/os/types_js.gs.js +95 -15
  156. package/dist/gs/os/types_js.gs.js.map +1 -1
  157. package/dist/gs/os/zero_copy_posix.gs.js +1 -1
  158. package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
  159. package/dist/gs/path/filepath/match.js.map +1 -1
  160. package/dist/gs/path/filepath/path.d.ts +5 -3
  161. package/dist/gs/path/filepath/path.js +65 -10
  162. package/dist/gs/path/filepath/path.js.map +1 -1
  163. package/dist/gs/reflect/index.d.ts +3 -2
  164. package/dist/gs/reflect/index.js +2 -1
  165. package/dist/gs/reflect/index.js.map +1 -1
  166. package/dist/gs/reflect/iter.js +2 -2
  167. package/dist/gs/reflect/iter.js.map +1 -1
  168. package/dist/gs/reflect/map.js +26 -0
  169. package/dist/gs/reflect/map.js.map +1 -1
  170. package/dist/gs/reflect/type.d.ts +24 -5
  171. package/dist/gs/reflect/type.js +390 -38
  172. package/dist/gs/reflect/type.js.map +1 -1
  173. package/dist/gs/reflect/types.d.ts +1 -0
  174. package/dist/gs/reflect/types.js +3 -1
  175. package/dist/gs/reflect/types.js.map +1 -1
  176. package/dist/gs/reflect/value.d.ts +4 -1
  177. package/dist/gs/reflect/value.js +39 -1
  178. package/dist/gs/reflect/value.js.map +1 -1
  179. package/dist/gs/reflect/visiblefields.js +1 -1
  180. package/dist/gs/reflect/visiblefields.js.map +1 -1
  181. package/dist/gs/runtime/debug/index.d.ts +39 -0
  182. package/dist/gs/runtime/debug/index.js +58 -0
  183. package/dist/gs/runtime/debug/index.js.map +1 -1
  184. package/dist/gs/runtime/pprof/index.d.ts +20 -0
  185. package/dist/gs/runtime/pprof/index.js +85 -0
  186. package/dist/gs/runtime/pprof/index.js.map +1 -0
  187. package/dist/gs/runtime/trace/index.d.ts +19 -0
  188. package/dist/gs/runtime/trace/index.js +64 -0
  189. package/dist/gs/runtime/trace/index.js.map +1 -0
  190. package/dist/gs/slices/slices.d.ts +24 -9
  191. package/dist/gs/slices/slices.js +229 -24
  192. package/dist/gs/slices/slices.js.map +1 -1
  193. package/dist/gs/sort/slice.gs.d.ts +5 -3
  194. package/dist/gs/sort/slice.gs.js +55 -17
  195. package/dist/gs/sort/slice.gs.js.map +1 -1
  196. package/dist/gs/strings/builder.js +26 -17
  197. package/dist/gs/strings/builder.js.map +1 -1
  198. package/dist/gs/strings/iter.js +140 -75
  199. package/dist/gs/strings/iter.js.map +1 -1
  200. package/dist/gs/strings/replace.js +2 -2
  201. package/dist/gs/strings/replace.js.map +1 -1
  202. package/dist/gs/strings/strings.js +52 -6
  203. package/dist/gs/strings/strings.js.map +1 -1
  204. package/dist/gs/sync/sync.d.ts +6 -3
  205. package/dist/gs/sync/sync.js +39 -11
  206. package/dist/gs/sync/sync.js.map +1 -1
  207. package/dist/gs/syscall/errors.d.ts +116 -112
  208. package/dist/gs/syscall/errors.js +38 -1
  209. package/dist/gs/syscall/errors.js.map +1 -1
  210. package/dist/gs/syscall/fs.d.ts +2 -8
  211. package/dist/gs/syscall/fs.js.map +1 -1
  212. package/dist/gs/syscall/js/index.js +20 -12
  213. package/dist/gs/syscall/js/index.js.map +1 -1
  214. package/dist/gs/syscall/types.d.ts +4 -1
  215. package/dist/gs/syscall/types.js.map +1 -1
  216. package/dist/gs/testing/testing.d.ts +4 -3
  217. package/dist/gs/testing/testing.js +21 -4
  218. package/dist/gs/testing/testing.js.map +1 -1
  219. package/dist/gs/time/time.js +22 -0
  220. package/dist/gs/time/time.js.map +1 -1
  221. package/dist/gs/unicode/unicode.js.map +1 -1
  222. package/dist/gs/unique/index.js +7 -2
  223. package/dist/gs/unique/index.js.map +1 -1
  224. package/go.mod +8 -8
  225. package/go.sum +14 -23
  226. package/gs/builtin/builtin.ts +364 -37
  227. package/gs/builtin/channel.ts +208 -38
  228. package/gs/builtin/defer.ts +13 -2
  229. package/gs/builtin/hostio.test.ts +1 -0
  230. package/gs/builtin/hostio.ts +38 -0
  231. package/gs/builtin/map.ts +46 -6
  232. package/gs/builtin/print.ts +12 -3
  233. package/gs/builtin/runtime-contract.test.ts +290 -10
  234. package/gs/builtin/slice.test.ts +70 -0
  235. package/gs/builtin/slice.ts +566 -255
  236. package/gs/builtin/type.ts +63 -10
  237. package/gs/builtin/varRef.ts +2 -0
  238. package/gs/bytes/buffer.gs.ts +28 -28
  239. package/gs/bytes/bytes.gs.ts +19 -10
  240. package/gs/bytes/bytes.test.ts +17 -0
  241. package/gs/bytes/iter.gs.ts +13 -14
  242. package/gs/compress/zlib/index.test.ts +28 -0
  243. package/gs/compress/zlib/index.ts +200 -0
  244. package/gs/compress/zlib/meta.json +3 -0
  245. package/gs/context/context.test.ts +36 -2
  246. package/gs/context/context.ts +9 -4
  247. package/gs/crypto/ecdh/index.test.ts +43 -0
  248. package/gs/crypto/ecdh/index.ts +274 -0
  249. package/gs/crypto/ed25519/index.test.ts +41 -0
  250. package/gs/crypto/ed25519/index.ts +238 -0
  251. package/gs/crypto/ed25519/meta.json +13 -0
  252. package/gs/crypto/internal/constanttime/index.test.ts +25 -0
  253. package/gs/crypto/internal/constanttime/index.ts +22 -0
  254. package/gs/crypto/rand/index.test.ts +89 -1
  255. package/gs/crypto/rand/index.ts +103 -1
  256. package/gs/crypto/rand/meta.json +4 -1
  257. package/gs/crypto/sha1/index.test.ts +28 -0
  258. package/gs/crypto/sha1/index.ts +130 -0
  259. package/gs/crypto/sha1/meta.json +8 -0
  260. package/gs/crypto/sha256/index.test.ts +78 -0
  261. package/gs/crypto/sha256/index.ts +150 -0
  262. package/gs/crypto/sha256/meta.json +9 -0
  263. package/gs/crypto/sha512/index.test.ts +31 -0
  264. package/gs/crypto/sha512/index.ts +161 -0
  265. package/gs/crypto/sha512/meta.json +11 -0
  266. package/gs/encoding/json/index.test.ts +25 -3
  267. package/gs/encoding/json/index.ts +21 -3
  268. package/gs/errors/errors.test.ts +4 -1
  269. package/gs/errors/errors.ts +32 -8
  270. package/gs/fmt/fmt.test.ts +23 -1
  271. package/gs/fmt/fmt.ts +76 -10
  272. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +62 -7
  273. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +78 -36
  274. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +32 -11
  275. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +122 -43
  276. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +31 -0
  277. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +518 -4
  278. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +6 -0
  279. package/gs/github.com/aperturerobotics/util/conc/index.test.ts +30 -0
  280. package/gs/github.com/aperturerobotics/util/conc/index.ts +172 -0
  281. package/gs/github.com/aperturerobotics/util/conc/meta.json +9 -0
  282. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.ts +1 -4
  283. package/gs/github.com/hack-pad/safejs/internal/catch/index.test.ts +35 -0
  284. package/gs/github.com/hack-pad/safejs/internal/catch/index.ts +65 -0
  285. package/gs/github.com/hack-pad/safejs/internal/catch/meta.json +9 -0
  286. package/gs/github.com/klauspost/compress/internal/le/index.test.ts +2 -1
  287. package/gs/github.com/klauspost/compress/internal/le/index.ts +6 -5
  288. package/gs/github.com/mr-tron/base58/base58/index.test.ts +70 -0
  289. package/gs/github.com/mr-tron/base58/base58/index.ts +231 -0
  290. package/gs/github.com/mr-tron/base58/base58/meta.json +3 -0
  291. package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +46 -0
  292. package/gs/github.com/zeebo/blake3/internal/consts/index.ts +26 -0
  293. package/gs/go/token/index.ts +17 -4
  294. package/gs/hash/fnv/index.test.ts +67 -0
  295. package/gs/hash/fnv/index.ts +351 -0
  296. package/gs/hash/fnv/meta.json +3 -0
  297. package/gs/hash/index.test.ts +37 -0
  298. package/gs/hash/index.ts +118 -0
  299. package/gs/hash/meta.json +5 -0
  300. package/gs/internal/byteorder/index.test.ts +6 -6
  301. package/gs/io/fs/readlink.ts +40 -48
  302. package/gs/io/fs/walk.ts +10 -2
  303. package/gs/io/io.test.ts +64 -0
  304. package/gs/io/io.ts +34 -13
  305. package/gs/iter/iter.ts +8 -2
  306. package/gs/maps/iter.ts +69 -26
  307. package/gs/maps/maps.test.ts +23 -0
  308. package/gs/maps/maps.ts +6 -6
  309. package/gs/math/bits/index.test.ts +20 -0
  310. package/gs/math/bits/index.ts +15 -28
  311. package/gs/mime/index.ts +8 -2
  312. package/gs/net/http/httptest/index.test.ts +85 -0
  313. package/gs/net/http/httptest/index.ts +113 -3
  314. package/gs/net/http/index.test.ts +159 -1
  315. package/gs/net/http/index.ts +515 -15
  316. package/gs/net/http/meta.json +6 -0
  317. package/gs/net/http/pprof/index.test.ts +47 -0
  318. package/gs/net/http/pprof/index.ts +65 -0
  319. package/gs/os/error.gs.ts +9 -10
  320. package/gs/os/error.test.ts +41 -0
  321. package/gs/os/file_unix_js.test.ts +55 -0
  322. package/gs/os/tempfile.gs.test.ts +37 -10
  323. package/gs/os/types_js.gs.ts +94 -15
  324. package/gs/os/zero_copy_posix.gs.ts +1 -2
  325. package/gs/path/filepath/match.ts +4 -1
  326. package/gs/path/filepath/meta.json +6 -0
  327. package/gs/path/filepath/path.test.ts +57 -2
  328. package/gs/path/filepath/path.ts +91 -12
  329. package/gs/reflect/field.test.ts +63 -0
  330. package/gs/reflect/index.ts +4 -1
  331. package/gs/reflect/iter.ts +2 -2
  332. package/gs/reflect/map.test.ts +24 -2
  333. package/gs/reflect/map.ts +35 -0
  334. package/gs/reflect/type.ts +543 -60
  335. package/gs/reflect/typefor.test.ts +100 -0
  336. package/gs/reflect/types.ts +3 -1
  337. package/gs/reflect/value.ts +50 -1
  338. package/gs/reflect/visiblefields.ts +1 -1
  339. package/gs/runtime/debug/index.test.ts +22 -1
  340. package/gs/runtime/debug/index.ts +88 -0
  341. package/gs/runtime/pprof/index.test.ts +36 -0
  342. package/gs/runtime/pprof/index.ts +104 -0
  343. package/gs/runtime/pprof/meta.json +6 -0
  344. package/gs/runtime/trace/index.test.ts +45 -0
  345. package/gs/runtime/trace/index.ts +97 -0
  346. package/gs/runtime/trace/meta.json +7 -0
  347. package/gs/slices/meta.json +2 -1
  348. package/gs/slices/slices.test.ts +86 -0
  349. package/gs/slices/slices.ts +284 -37
  350. package/gs/sort/slice.gs.ts +73 -23
  351. package/gs/sort/slice.test.ts +40 -0
  352. package/gs/strings/builder.test.ts +8 -0
  353. package/gs/strings/builder.ts +29 -17
  354. package/gs/strings/iter.test.ts +5 -7
  355. package/gs/strings/iter.ts +146 -71
  356. package/gs/strings/replace.test.ts +1 -4
  357. package/gs/strings/replace.ts +6 -6
  358. package/gs/strings/strings.test.ts +4 -0
  359. package/gs/strings/strings.ts +54 -6
  360. package/gs/sync/meta.json +1 -0
  361. package/gs/sync/sync.test.ts +57 -1
  362. package/gs/sync/sync.ts +45 -13
  363. package/gs/syscall/errors.ts +158 -115
  364. package/gs/syscall/fs.ts +8 -8
  365. package/gs/syscall/js/index.ts +49 -22
  366. package/gs/syscall/net.test.ts +26 -0
  367. package/gs/syscall/types.ts +7 -2
  368. package/gs/testing/testing.test.ts +56 -0
  369. package/gs/testing/testing.ts +27 -10
  370. package/gs/time/meta.json +2 -2
  371. package/gs/time/time.test.ts +4 -0
  372. package/gs/time/time.ts +33 -2
  373. package/gs/unicode/unicode.test.ts +14 -3
  374. package/gs/unicode/unicode.ts +1 -5
  375. package/gs/unique/index.ts +9 -2
  376. package/package.json +3 -3
@@ -215,11 +215,11 @@ func TestCompilePackagesLazilyInitializesCrossFilePackageVars(t *testing.T) {
215
215
  }
216
216
  text := string(content)
217
217
  for _, want := range []string{
218
- "export let two: holder = undefined as unknown as holder",
218
+ "export var two: holder = undefined as unknown as holder",
219
219
  "export function __goscript_get_two(): holder",
220
- "export let remoteZero: __goscript_a.remote = undefined as unknown as __goscript_a.remote",
220
+ "export var remoteZero: __goscript_a.remote = undefined as unknown as __goscript_a.remote",
221
221
  "export function __goscript_get_remoteZero(): __goscript_a.remote",
222
- "__goscript_a.one",
222
+ "__goscript_a.__goscript_get_one()",
223
223
  } {
224
224
  if !strings.Contains(text, want) {
225
225
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -235,6 +235,44 @@ func TestCompilePackagesLazilyInitializesCrossFilePackageVars(t *testing.T) {
235
235
  }
236
236
  }
237
237
 
238
+ func TestCompilePackagesLazilyInitializesSameFileLaterPackageVars(t *testing.T) {
239
+ moduleDir := writePackageGraphFixture(t, map[string]string{
240
+ "go.mod": "module example.test/lazylatervars\n\ngo 1.25.3\n",
241
+ "main.go": strings.Join([]string{
242
+ "package main",
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) }",
247
+ "",
248
+ }, "\n"),
249
+ })
250
+ outputDir := filepath.Join(t.TempDir(), "output")
251
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
252
+ if err != nil {
253
+ t.Fatal(err.Error())
254
+ }
255
+
256
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
257
+ t.Fatal(err.Error())
258
+ }
259
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazylatervars", "main.gs.ts")
260
+ content, err := os.ReadFile(outputFile)
261
+ if err != nil {
262
+ t.Fatal(err.Error())
263
+ }
264
+ text := string(content)
265
+ for _, want := range []string{
266
+ "export var 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}))",
269
+ } {
270
+ if !strings.Contains(text, want) {
271
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
272
+ }
273
+ }
274
+ }
275
+
238
276
  func TestCompilePackagesLazilyInitializesFunctionBodyPackageVarDependencies(t *testing.T) {
239
277
  moduleDir := writePackageGraphFixture(t, map[string]string{
240
278
  "go.mod": "module example.test/lazybodyvars\n\ngo 1.25.3\n",
@@ -267,7 +305,7 @@ func TestCompilePackagesLazilyInitializesFunctionBodyPackageVarDependencies(t *t
267
305
  }
268
306
  text := string(content)
269
307
  for _, want := range []string{
270
- "export let first: Point | $.VarRef<Point> | null = undefined as unknown as Point | $.VarRef<Point> | null",
308
+ "export var first: Point | $.VarRef<Point> | null = undefined as unknown as Point | $.VarRef<Point> | null",
271
309
  "export function __goscript_get_first(): Point | $.VarRef<Point> | null",
272
310
  "function __goscript_get___goscriptTuple",
273
311
  "export let later: number = 7",
@@ -278,17 +316,83 @@ func TestCompilePackagesLazilyInitializesFunctionBodyPackageVarDependencies(t *t
278
316
  }
279
317
  }
280
318
 
281
- func TestCompilePackagesEmitsShadowedBuiltinCalls(t *testing.T) {
319
+ func TestCompilePackagesLazilyInitializesEffectFreeTypeForFromCrossFileInit(t *testing.T) {
282
320
  moduleDir := writePackageGraphFixture(t, map[string]string{
283
- "go.mod": "module example.test/shadowbuiltin\n\ngo 1.25.3\n",
321
+ "go.mod": "module example.test/lazytypefor\n\ngo 1.25.3\n",
322
+ "a.go": strings.Join([]string{
323
+ "package main",
324
+ "import \"reflect\"",
325
+ "var stringType = reflect.TypeFor[string]()",
326
+ "var _ = marker",
327
+ "",
328
+ }, "\n"),
329
+ "b.go": strings.Join([]string{
330
+ "package main",
331
+ "func readStringType() { println(stringType != nil) }",
332
+ "func main() {}",
333
+ "",
334
+ }, "\n"),
335
+ "c.go": strings.Join([]string{
336
+ "package main",
337
+ "var marker = 1",
338
+ "func init() { readStringType() }",
339
+ "",
340
+ }, "\n"),
341
+ })
342
+ outputDir := filepath.Join(t.TempDir(), "output")
343
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
344
+ if err != nil {
345
+ t.Fatal(err.Error())
346
+ }
347
+
348
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
349
+ t.Fatal(err.Error())
350
+ }
351
+ aFile := filepath.Join(outputDir, "@goscript", "example.test", "lazytypefor", "a.gs.ts")
352
+ aContent, err := os.ReadFile(aFile)
353
+ if err != nil {
354
+ t.Fatal(err.Error())
355
+ }
356
+ aText := string(aContent)
357
+ for _, want := range []string{
358
+ "export var stringType: reflect.Type | null = undefined as unknown as reflect.Type | null",
359
+ "export function __goscript_get_stringType(): reflect.Type | null",
360
+ "stringType = reflect.TypeFor",
361
+ } {
362
+ if !strings.Contains(aText, want) {
363
+ t.Fatalf("missing %q in generated a.go output:\n%s", want, aText)
364
+ }
365
+ }
366
+ bFile := filepath.Join(outputDir, "@goscript", "example.test", "lazytypefor", "b.gs.ts")
367
+ bContent, err := os.ReadFile(bFile)
368
+ if err != nil {
369
+ t.Fatal(err.Error())
370
+ }
371
+ bText := string(bContent)
372
+ if !strings.Contains(bText, "__goscript_a.__goscript_get_stringType() != null") {
373
+ t.Fatalf("missing lazy TypeFor package var getter use:\n%s", bText)
374
+ }
375
+ if strings.Contains(bText, "__goscript_a.stringType != null") {
376
+ t.Fatalf("cross-file init still reads TypeFor package var directly:\n%s", bText)
377
+ }
378
+ }
379
+
380
+ func TestCompilePackagesInitializesLazyAsyncPackageVarsBeforeInit(t *testing.T) {
381
+ moduleDir := writePackageGraphFixture(t, map[string]string{
382
+ "go.mod": "module example.test/lazyasyncvars\n\ngo 1.25.3\n",
284
383
  "main.go": strings.Join([]string{
285
- "package shadowbuiltin",
286
- "type Value struct {",
287
- " N int",
288
- "}",
289
- "func Build(new func() (*Value, error)) (*Value, error) {",
290
- " return new()",
384
+ "package main",
385
+ "import \"sync\"",
386
+ "var lock sync.Mutex",
387
+ "var first = makeFirst()",
388
+ "func makeFirst() int {",
389
+ " lock.Lock()",
390
+ " defer lock.Unlock()",
391
+ " return later",
291
392
  "}",
393
+ "var later = 7",
394
+ "func init() { println(first) }",
395
+ "func main() {}",
292
396
  "",
293
397
  }, "\n"),
294
398
  })
@@ -301,25 +405,36 @@ func TestCompilePackagesEmitsShadowedBuiltinCalls(t *testing.T) {
301
405
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
302
406
  t.Fatal(err.Error())
303
407
  }
304
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "shadowbuiltin", "main.gs.ts")
408
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazyasyncvars", "main.gs.ts")
305
409
  content, err := os.ReadFile(outputFile)
306
410
  if err != nil {
307
411
  t.Fatal(err.Error())
308
412
  }
309
413
  text := string(content)
310
- if !strings.Contains(text, "return await _new!()") {
311
- t.Fatalf("shadowed builtin call was not emitted as a callable value:\n%s", text)
414
+ for _, want := range []string{
415
+ "async function __goscript_init_first(): globalThis.Promise<void>",
416
+ "first = await makeFirst()",
417
+ "export function __goscript_get_first(): number",
418
+ "await __goscript_init_first()",
419
+ "__goscriptInit0()",
420
+ } {
421
+ if !strings.Contains(text, want) {
422
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
423
+ }
312
424
  }
313
425
  }
314
426
 
315
- func TestCompilePackagesQuotesRawStringLiterals(t *testing.T) {
427
+ func TestCompilePackagesAssignsLazyPackageVarsDirectly(t *testing.T) {
316
428
  moduleDir := writePackageGraphFixture(t, map[string]string{
317
- "go.mod": "module example.test/rawstrings\n\ngo 1.25.3\n",
429
+ "go.mod": "module example.test/lazyassign\n\ngo 1.25.3\n",
318
430
  "main.go": strings.Join([]string{
319
- "package rawstrings",
320
- "func Values() (string, string) {",
321
- " return `\\u00`, `invalid escape char after \\`",
431
+ "package main",
432
+ "var table = []int{later}",
433
+ "var later = 1",
434
+ "func init() {",
435
+ " table = append(table, 2)",
322
436
  "}",
437
+ "func main() { println(len(table)) }",
323
438
  "",
324
439
  }, "\n"),
325
440
  })
@@ -332,173 +447,1383 @@ func TestCompilePackagesQuotesRawStringLiterals(t *testing.T) {
332
447
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
333
448
  t.Fatal(err.Error())
334
449
  }
335
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "rawstrings", "main.gs.ts")
450
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazyassign", "main.gs.ts")
336
451
  content, err := os.ReadFile(outputFile)
337
452
  if err != nil {
338
453
  t.Fatal(err.Error())
339
454
  }
340
455
  text := string(content)
341
- if !strings.Contains(text, `return ["\\u00", "invalid escape char after \\"]`) {
342
- t.Fatalf("raw string literals were not quoted for TypeScript:\n%s", text)
456
+ if !strings.Contains(text, "__goscript_set_table($.append(__goscript_get_table(), 2))") {
457
+ t.Fatalf("missing lazy package var assignment through setter:\n%s", text)
458
+ }
459
+ if strings.Contains(text, "__goscript_get_table() =") {
460
+ t.Fatalf("lazy getter used as assignment target:\n%s", text)
343
461
  }
344
462
  }
345
463
 
346
- func TestCompilePackagesUsesEmbedOverride(t *testing.T) {
464
+ func TestCompilePackagesAssignsImportedPackageVarsThroughSetters(t *testing.T) {
347
465
  moduleDir := writePackageGraphFixture(t, map[string]string{
348
- "go.mod": "module example.test/embedblank\n\ngo 1.25.3\n",
349
- "version.txt": "1.2.3\n",
350
- "version..txt": "4.5.6\n",
351
- "binary.bin": string([]byte{0x00, 0xff, 0x80, 0x41}),
466
+ "go.mod": "module example.test/pkgvarassign\n\ngo 1.25.3\n",
467
+ "dep/dep.go": strings.Join([]string{
468
+ "package dep",
469
+ "var Count int",
470
+ "",
471
+ }, "\n"),
352
472
  "main.go": strings.Join([]string{
353
- "package embedblank",
354
- "import _ \"embed\"",
355
- "//go:embed version.txt",
356
- "var Version string",
357
- "//go:embed version..txt",
358
- "var Dotted string",
359
- "//go:embed binary.bin",
360
- "var Binary []byte",
361
- "func GetVersion() string {",
362
- " return Version",
473
+ "package main",
474
+ "import \"example.test/pkgvarassign/dep\"",
475
+ "func bump() {",
476
+ " dep.Count = 1",
477
+ " dep.Count += 16",
478
+ " dep.Count++",
363
479
  "}",
364
480
  "",
365
481
  }, "\n"),
366
482
  })
367
483
  outputDir := filepath.Join(t.TempDir(), "output")
368
- comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir, AllDependencies: true}, nil, nil)
484
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
369
485
  if err != nil {
370
486
  t.Fatal(err.Error())
371
487
  }
372
488
 
373
- result, err := comp.CompilePackages(context.Background(), ".")
489
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
490
+ t.Fatal(err.Error())
491
+ }
492
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "pkgvarassign", "main.gs.ts")
493
+ content, err := os.ReadFile(outputFile)
374
494
  if err != nil {
375
495
  t.Fatal(err.Error())
376
496
  }
377
- if !slices.Contains(result.CopiedPackages, "embed") {
378
- t.Fatalf("embed override was not copied: %#v", result.CopiedPackages)
497
+ text := string(content)
498
+ for _, want := range []string{
499
+ "dep.__goscript_set_Count(1)",
500
+ "dep.__goscript_set_Count(dep.Count + 16)",
501
+ "dep.__goscript_set_Count(dep.Count + 1)",
502
+ } {
503
+ if !strings.Contains(text, want) {
504
+ t.Fatalf("missing imported package var setter assignment %q:\n%s", want, text)
505
+ }
379
506
  }
380
- if _, err := os.Stat(filepath.Join(outputDir, "@goscript", "embed", "index.ts")); err != nil {
381
- t.Fatalf("embed override missing from output: %v", err)
507
+ if strings.Contains(text, "dep.Count +=") || strings.Contains(text, "dep.Count++") {
508
+ t.Fatalf("imported package var assigned directly:\n%s", text)
382
509
  }
383
- if _, err := os.Stat(filepath.Join(outputDir, "@goscript", "embed", "embed.gs.ts")); !os.IsNotExist(err) {
384
- t.Fatalf("stdlib embed was emitted instead of override: %v", err)
510
+ }
511
+
512
+ func TestCompilePackagesAliasesForInitShortDeclShadow(t *testing.T) {
513
+ moduleDir := writePackageGraphFixture(t, map[string]string{
514
+ "go.mod": "module example.test/forinitshadow\n\ngo 1.25.3\n",
515
+ "main.go": strings.Join([]string{
516
+ "package main",
517
+ "func checksum(n int) int {",
518
+ " total := 0",
519
+ " for n := n - 4; total <= n; total++ {",
520
+ " total += n",
521
+ " }",
522
+ " return total + n",
523
+ "}",
524
+ "",
525
+ }, "\n"),
526
+ })
527
+ outputDir := filepath.Join(t.TempDir(), "output")
528
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
529
+ if err != nil {
530
+ t.Fatal(err.Error())
385
531
  }
386
- content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "embedblank", "main.gs.ts"))
532
+
533
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
534
+ t.Fatal(err.Error())
535
+ }
536
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "forinitshadow", "main.gs.ts")
537
+ content, err := os.ReadFile(outputFile)
387
538
  if err != nil {
388
539
  t.Fatal(err.Error())
389
540
  }
390
- if !strings.Contains(string(content), "export let Version: string = \"1.2.3\\n\"") {
391
- t.Fatalf("embedded string content was not emitted:\n%s", string(content))
541
+ text := string(content)
542
+ if strings.Contains(text, "for (let n = n - 4") {
543
+ t.Fatalf("for init short declaration references itself:\n%s", text)
392
544
  }
393
- if !strings.Contains(string(content), "export let Dotted: string = \"4.5.6\\n\"") {
394
- t.Fatalf("embedded dotted filename content was not emitted:\n%s", string(content))
545
+ if !regexp.MustCompile(`let __goscriptShadow\d+ = n\s+for \(let __goscriptShadow\d+ = __goscriptShadow\d+ - 4; total <= __goscriptShadow\d+; total\+\+\)`).MatchString(text) {
546
+ t.Fatalf("missing aliased for init shadow declaration:\n%s", text)
395
547
  }
396
- if !strings.Contains(string(content), "export let Binary: $.Slice<number> = new Uint8Array([0, 255, 128, 65])") {
397
- t.Fatalf("embedded binary content was not emitted as byte values:\n%s", string(content))
548
+ if !strings.Contains(text, "return total + n") {
549
+ t.Fatalf("outer n was not preserved after the loop:\n%s", text)
398
550
  }
399
551
  }
400
552
 
401
- func TestCompilePackagesEmitsPackageLocalImport(t *testing.T) {
553
+ func TestCompilePackagesReadsShadowedVarRefStructFieldsOnce(t *testing.T) {
402
554
  moduleDir := writePackageGraphFixture(t, map[string]string{
403
- "go.mod": "module example.test/imports\n\ngo 1.25.3\n",
555
+ "go.mod": "module example.test/shadowvarreffield\n\ngo 1.25.3\n",
404
556
  "main.go": strings.Join([]string{
405
557
  "package main",
406
- "import \"example.test/imports/subpkg\"",
407
- "func main() {",
408
- " var b subpkg.Builder",
409
- " b.Set(\"built\")",
410
- " println(b.Value)",
411
- " println(subpkg.Greet(\"world\"))",
412
- " println(localMessage())",
558
+ "type key struct { pad []byte }",
559
+ "func fill(k *key) error { return nil }",
560
+ "func size() int {",
561
+ " var key key",
562
+ " if err := fill(&key); err != nil {",
563
+ " return 0",
564
+ " }",
565
+ " return len(key.pad)",
413
566
  "}",
567
+ "func main() { println(size()) }",
414
568
  "",
415
569
  }, "\n"),
416
- "helper.go": strings.Join([]string{
570
+ })
571
+ outputDir := filepath.Join(t.TempDir(), "output")
572
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
573
+ if err != nil {
574
+ t.Fatal(err.Error())
575
+ }
576
+
577
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
578
+ t.Fatal(err.Error())
579
+ }
580
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "shadowvarreffield", "main.gs.ts")
581
+ content, err := os.ReadFile(outputFile)
582
+ if err != nil {
583
+ t.Fatal(err.Error())
584
+ }
585
+ text := string(content)
586
+ if !strings.Contains(text, "$.len(__goscriptShadow0.value.pad)") {
587
+ t.Fatalf("missing single dereference field read:\n%s", text)
588
+ }
589
+ if strings.Contains(text, ".value.value.pad") {
590
+ t.Fatalf("shadowed VarRef struct field was dereferenced twice:\n%s", text)
591
+ }
592
+ }
593
+
594
+ func TestCompilePackagesWrapsChannelSendInterfaceValues(t *testing.T) {
595
+ moduleDir := writePackageGraphFixture(t, map[string]string{
596
+ "go.mod": "module example.test/chansendiface\n\ngo 1.25.3\n",
597
+ "main.go": strings.Join([]string{
417
598
  "package main",
418
- "func localMessage() string {",
419
- " return \"from helper\"",
420
- "}",
421
- "",
422
- }, "\n"),
423
- "subpkg/subpkg.go": strings.Join([]string{
424
- "package subpkg",
425
- "type Builder struct {",
426
- " Value string",
427
- "}",
428
- "func (b *Builder) Set(value string) {",
429
- " b.Value = value",
430
- "}",
431
- "func Greet(name string) string {",
432
- " return \"Hello, \" + name",
599
+ "type item interface { Name() string }",
600
+ "type concrete struct{}",
601
+ "func (*concrete) Name() string { return \"ok\" }",
602
+ "func send(ch chan item) {",
603
+ " v := &concrete{}",
604
+ " ch <- v",
433
605
  "}",
606
+ "func main() {}",
434
607
  "",
435
608
  }, "\n"),
436
609
  })
437
610
  outputDir := filepath.Join(t.TempDir(), "output")
438
- comp, err := NewCompiler(&Config{
439
- Dir: moduleDir,
440
- OutputPath: outputDir,
441
- AllDependencies: true,
442
- }, nil, nil)
611
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
443
612
  if err != nil {
444
613
  t.Fatal(err.Error())
445
614
  }
446
615
 
447
- result, err := comp.CompilePackages(context.Background(), ".")
448
- if err != nil {
616
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
449
617
  t.Fatal(err.Error())
450
618
  }
451
- if result == nil || len(result.CompiledPackages) != 2 {
452
- t.Fatalf("unexpected result: %#v", result)
453
- }
454
- mainFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "main.gs.ts")
455
- mainContent, err := os.ReadFile(mainFile)
619
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "chansendiface", "main.gs.ts")
620
+ content, err := os.ReadFile(outputFile)
456
621
  if err != nil {
457
622
  t.Fatal(err.Error())
458
623
  }
459
- if !strings.Contains(string(mainContent), "import * as subpkg from \"@goscript/example.test/imports/subpkg/index.js\"") {
460
- t.Fatalf("missing package-local import:\n%s", string(mainContent))
624
+ text := string(content)
625
+ if !strings.Contains(text, "$.chanSend(ch, $.interfaceValue<item | null>(v, \"*main.concrete\"))") {
626
+ t.Fatalf("missing interface wrapper for channel send:\n%s", text)
461
627
  }
462
- if !strings.Contains(string(mainContent), "let b: $.VarRef<subpkg.Builder> = $.varRef($.markAsStructValue(new subpkg.Builder()))") {
463
- t.Fatalf("missing imported struct zero value qualification:\n%s", string(mainContent))
628
+ }
629
+
630
+ func TestCompilePackagesBindsFuncLiteralVarRefParams(t *testing.T) {
631
+ moduleDir := writePackageGraphFixture(t, map[string]string{
632
+ "go.mod": "module example.test/funclitvarref\n\ngo 1.25.3\n",
633
+ "main.go": strings.Join([]string{
634
+ "package main",
635
+ "type words []int",
636
+ "func (w *words) Add(v int) { *w = append(*w, v) }",
637
+ "func main() {",
638
+ " addLen := func(w words) int {",
639
+ " w.Add(7)",
640
+ " return len(w)",
641
+ " }",
642
+ " println(addLen(nil))",
643
+ "}",
644
+ "",
645
+ }, "\n"),
646
+ })
647
+ outputDir := filepath.Join(t.TempDir(), "output")
648
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
649
+ if err != nil {
650
+ t.Fatal(err.Error())
464
651
  }
465
- if !strings.Contains(string(mainContent), "import * as __goscript_helper from \"./helper.gs.ts\"") ||
466
- !strings.Contains(string(mainContent), "$.println(__goscript_helper.localMessage())") {
467
- t.Fatalf("missing same-package helper import:\n%s", string(mainContent))
652
+
653
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
654
+ t.Fatal(err.Error())
468
655
  }
469
- indexFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "subpkg", "index.ts")
470
- indexContent, err := os.ReadFile(indexFile)
656
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "funclitvarref", "main.gs.ts")
657
+ content, err := os.ReadFile(outputFile)
471
658
  if err != nil {
472
659
  t.Fatal(err.Error())
473
660
  }
474
- if string(indexContent) != "export { Builder, Greet } from \"./subpkg.gs.ts\"\n" {
475
- t.Fatalf("unexpected subpkg index:\n%s", string(indexContent))
661
+ text := string(content)
662
+ for _, want := range []string{
663
+ "(__goscriptParam0: words): number => {",
664
+ "let w: $.VarRef<words> = $.varRef(__goscriptParam0)",
665
+ "words_Add(w, 7)",
666
+ } {
667
+ if !strings.Contains(text, want) {
668
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
669
+ }
670
+ }
671
+ }
672
+
673
+ func TestCompilePackagesAnnotatesNewPointerShortDecls(t *testing.T) {
674
+ moduleDir := writePackageGraphFixture(t, map[string]string{
675
+ "go.mod": "module example.test/newptrdecl\n\ngo 1.25.3\n",
676
+ "main.go": strings.Join([]string{
677
+ "package main",
678
+ "type OID []int",
679
+ "func use(*OID) {}",
680
+ "func main() {",
681
+ " oid := new(OID)",
682
+ " if len(*oid) == 0 {",
683
+ " oid = nil",
684
+ " }",
685
+ " use(oid)",
686
+ "}",
687
+ "",
688
+ }, "\n"),
689
+ })
690
+ outputDir := filepath.Join(t.TempDir(), "output")
691
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
692
+ if err != nil {
693
+ t.Fatal(err.Error())
694
+ }
695
+
696
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
697
+ t.Fatal(err.Error())
698
+ }
699
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "newptrdecl", "main.gs.ts")
700
+ content, err := os.ReadFile(outputFile)
701
+ if err != nil {
702
+ t.Fatal(err.Error())
703
+ }
704
+ text := string(content)
705
+ for _, want := range []string{
706
+ "let oid: $.VarRef<OID> | null = $.varRef<OID>(null as OID)",
707
+ "oid = null",
708
+ "use(oid)",
709
+ } {
710
+ if !strings.Contains(text, want) {
711
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
712
+ }
713
+ }
714
+ }
715
+
716
+ func TestCompilePackagesEmitsShadowedBuiltinCalls(t *testing.T) {
717
+ moduleDir := writePackageGraphFixture(t, map[string]string{
718
+ "go.mod": "module example.test/shadowbuiltin\n\ngo 1.25.3\n",
719
+ "main.go": strings.Join([]string{
720
+ "package shadowbuiltin",
721
+ "type Value struct {",
722
+ " N int",
723
+ "}",
724
+ "func Build(new func() (*Value, error)) (*Value, error) {",
725
+ " return new()",
726
+ "}",
727
+ "",
728
+ }, "\n"),
729
+ })
730
+ outputDir := filepath.Join(t.TempDir(), "output")
731
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
732
+ if err != nil {
733
+ t.Fatal(err.Error())
734
+ }
735
+
736
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
737
+ t.Fatal(err.Error())
738
+ }
739
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "shadowbuiltin", "main.gs.ts")
740
+ content, err := os.ReadFile(outputFile)
741
+ if err != nil {
742
+ t.Fatal(err.Error())
743
+ }
744
+ text := string(content)
745
+ if !strings.Contains(text, "return await _new!()") {
746
+ t.Fatalf("shadowed builtin call was not emitted as a callable value:\n%s", text)
747
+ }
748
+ }
749
+
750
+ func TestCompilePackagesQuotesRawStringLiterals(t *testing.T) {
751
+ moduleDir := writePackageGraphFixture(t, map[string]string{
752
+ "go.mod": "module example.test/rawstrings\n\ngo 1.25.3\n",
753
+ "main.go": strings.Join([]string{
754
+ "package rawstrings",
755
+ "func Values() (string, string) {",
756
+ " return `\\u00`, `invalid escape char after \\`",
757
+ "}",
758
+ "",
759
+ }, "\n"),
760
+ })
761
+ outputDir := filepath.Join(t.TempDir(), "output")
762
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
763
+ if err != nil {
764
+ t.Fatal(err.Error())
765
+ }
766
+
767
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
768
+ t.Fatal(err.Error())
769
+ }
770
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "rawstrings", "main.gs.ts")
771
+ content, err := os.ReadFile(outputFile)
772
+ if err != nil {
773
+ t.Fatal(err.Error())
774
+ }
775
+ text := string(content)
776
+ if !strings.Contains(text, `return ["\\u00", "invalid escape char after \\"]`) {
777
+ t.Fatalf("raw string literals were not quoted for TypeScript:\n%s", text)
778
+ }
779
+ }
780
+
781
+ func TestCompilePackagesEmitsBinaryStringLiterals(t *testing.T) {
782
+ moduleDir := writePackageGraphFixture(t, map[string]string{
783
+ "go.mod": "module example.test/binarystrings\n\ngo 1.25.3\n",
784
+ "main.go": strings.Join([]string{
785
+ "package binarystrings",
786
+ "const DecodeMap = \"\\xff\\x80A\"",
787
+ "func Value() string {",
788
+ " return DecodeMap",
789
+ "}",
790
+ "",
791
+ }, "\n"),
792
+ })
793
+ outputDir := filepath.Join(t.TempDir(), "output")
794
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
795
+ if err != nil {
796
+ t.Fatal(err.Error())
797
+ }
798
+
799
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
800
+ t.Fatal(err.Error())
801
+ }
802
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "binarystrings", "main.gs.ts")
803
+ content, err := os.ReadFile(outputFile)
804
+ if err != nil {
805
+ t.Fatal(err.Error())
806
+ }
807
+ if !strings.Contains(string(content), "$.bytesToString(new Uint8Array([255, 128, 65]))") {
808
+ t.Fatalf("binary string literal was not emitted as byte-backed string:\n%s", string(content))
809
+ }
810
+ }
811
+
812
+ func TestCompilePackagesUsesEmbedOverride(t *testing.T) {
813
+ moduleDir := writePackageGraphFixture(t, map[string]string{
814
+ "go.mod": "module example.test/embedblank\n\ngo 1.25.3\n",
815
+ "version.txt": "1.2.3\n",
816
+ "version..txt": "4.5.6\n",
817
+ "binary.bin": string([]byte{0x00, 0xff, 0x80, 0x41}),
818
+ "main.go": strings.Join([]string{
819
+ "package embedblank",
820
+ "import _ \"embed\"",
821
+ "//go:embed version.txt",
822
+ "var Version string",
823
+ "//go:embed version..txt",
824
+ "var Dotted string",
825
+ "//go:embed binary.bin",
826
+ "var Binary []byte",
827
+ "func GetVersion() string {",
828
+ " return Version",
829
+ "}",
830
+ "",
831
+ }, "\n"),
832
+ })
833
+ outputDir := filepath.Join(t.TempDir(), "output")
834
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir, AllDependencies: true}, nil, nil)
835
+ if err != nil {
836
+ t.Fatal(err.Error())
837
+ }
838
+
839
+ result, err := comp.CompilePackages(context.Background(), ".")
840
+ if err != nil {
841
+ t.Fatal(err.Error())
842
+ }
843
+ if !slices.Contains(result.CopiedPackages, "embed") {
844
+ t.Fatalf("embed override was not copied: %#v", result.CopiedPackages)
845
+ }
846
+ if _, err := os.Stat(filepath.Join(outputDir, "@goscript", "embed", "index.ts")); err != nil {
847
+ t.Fatalf("embed override missing from output: %v", err)
848
+ }
849
+ if _, err := os.Stat(filepath.Join(outputDir, "@goscript", "embed", "embed.gs.ts")); !os.IsNotExist(err) {
850
+ t.Fatalf("stdlib embed was emitted instead of override: %v", err)
851
+ }
852
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "embedblank", "main.gs.ts"))
853
+ if err != nil {
854
+ t.Fatal(err.Error())
855
+ }
856
+ if !strings.Contains(string(content), "export let Version: string = \"1.2.3\\n\"") {
857
+ t.Fatalf("embedded string content was not emitted:\n%s", string(content))
858
+ }
859
+ if !strings.Contains(string(content), "export let Dotted: string = \"4.5.6\\n\"") {
860
+ t.Fatalf("embedded dotted filename content was not emitted:\n%s", string(content))
861
+ }
862
+ if !strings.Contains(string(content), "export let Binary: $.Slice<number> = new Uint8Array([0, 255, 128, 65])") {
863
+ t.Fatalf("embedded binary content was not emitted as byte values:\n%s", string(content))
864
+ }
865
+ }
866
+
867
+ func TestCompilePackagesEmitsPackageLocalImport(t *testing.T) {
868
+ moduleDir := writePackageGraphFixture(t, map[string]string{
869
+ "go.mod": "module example.test/imports\n\ngo 1.25.3\n",
870
+ "main.go": strings.Join([]string{
871
+ "package main",
872
+ "import \"example.test/imports/subpkg\"",
873
+ "func main() {",
874
+ " var b subpkg.Builder",
875
+ " b.Set(\"built\")",
876
+ " println(b.Value)",
877
+ " println(subpkg.Greet(\"world\"))",
878
+ " println(localMessage())",
879
+ "}",
880
+ "",
881
+ }, "\n"),
882
+ "helper.go": strings.Join([]string{
883
+ "package main",
884
+ "func localMessage() string {",
885
+ " return \"from helper\"",
886
+ "}",
887
+ "",
888
+ }, "\n"),
889
+ "subpkg/subpkg.go": strings.Join([]string{
890
+ "package subpkg",
891
+ "type Builder struct {",
892
+ " Value string",
893
+ "}",
894
+ "func (b *Builder) Set(value string) {",
895
+ " b.Value = value",
896
+ "}",
897
+ "func Greet(name string) string {",
898
+ " return \"Hello, \" + name",
899
+ "}",
900
+ "",
901
+ }, "\n"),
902
+ })
903
+ outputDir := filepath.Join(t.TempDir(), "output")
904
+ comp, err := NewCompiler(&Config{
905
+ Dir: moduleDir,
906
+ OutputPath: outputDir,
907
+ AllDependencies: true,
908
+ }, nil, nil)
909
+ if err != nil {
910
+ t.Fatal(err.Error())
911
+ }
912
+
913
+ result, err := comp.CompilePackages(context.Background(), ".")
914
+ if err != nil {
915
+ t.Fatal(err.Error())
916
+ }
917
+ if result == nil || len(result.CompiledPackages) != 2 {
918
+ t.Fatalf("unexpected result: %#v", result)
919
+ }
920
+ mainFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "main.gs.ts")
921
+ mainContent, err := os.ReadFile(mainFile)
922
+ if err != nil {
923
+ t.Fatal(err.Error())
924
+ }
925
+ if !strings.Contains(string(mainContent), "import * as subpkg from \"@goscript/example.test/imports/subpkg/index.js\"") {
926
+ t.Fatalf("missing package-local import:\n%s", string(mainContent))
927
+ }
928
+ if !strings.Contains(string(mainContent), "let b: $.VarRef<subpkg.Builder> = $.varRef($.markAsStructValue(new subpkg.Builder()))") {
929
+ t.Fatalf("missing imported struct zero value qualification:\n%s", string(mainContent))
930
+ }
931
+ if !strings.Contains(string(mainContent), "import * as __goscript_helper from \"./helper.gs.ts\"") ||
932
+ !strings.Contains(string(mainContent), "$.println(__goscript_helper.localMessage())") {
933
+ t.Fatalf("missing same-package helper import:\n%s", string(mainContent))
934
+ }
935
+ indexFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "subpkg", "index.ts")
936
+ indexContent, err := os.ReadFile(indexFile)
937
+ if err != nil {
938
+ t.Fatal(err.Error())
939
+ }
940
+ if string(indexContent) != "export { Builder, Greet } from \"./subpkg.gs.ts\"\n" {
941
+ t.Fatalf("unexpected subpkg index:\n%s", string(indexContent))
476
942
  }
477
943
  mainIndexFile := filepath.Join(outputDir, "@goscript", "example.test", "imports", "index.ts")
478
944
  mainIndexContent, err := os.ReadFile(mainIndexFile)
479
945
  if err != nil {
480
946
  t.Fatal(err.Error())
481
947
  }
482
- if strings.Contains(string(mainIndexContent), "localMessage") {
483
- t.Fatalf("unexported helper leaked into package index:\n%s", string(mainIndexContent))
948
+ if strings.Contains(string(mainIndexContent), "localMessage") {
949
+ t.Fatalf("unexported helper leaked into package index:\n%s", string(mainIndexContent))
950
+ }
951
+ }
952
+
953
+ func TestCompilePackagesEmitsTypeOnlyLocalImports(t *testing.T) {
954
+ moduleDir := writePackageGraphFixture(t, map[string]string{
955
+ "go.mod": "module example.test/typeonlylocal\n\ngo 1.25.3\n",
956
+ "a.go": strings.Join([]string{
957
+ "package main",
958
+ "type Acceptor interface {",
959
+ " Accept(Payload)",
960
+ "}",
961
+ "",
962
+ }, "\n"),
963
+ "payload.go": strings.Join([]string{
964
+ "package main",
965
+ "type Payload struct {",
966
+ " Value string",
967
+ "}",
968
+ "",
969
+ }, "\n"),
970
+ })
971
+ outputDir := filepath.Join(t.TempDir(), "output")
972
+ comp, err := NewCompiler(&Config{
973
+ Dir: moduleDir,
974
+ OutputPath: outputDir,
975
+ }, nil, nil)
976
+ if err != nil {
977
+ t.Fatal(err.Error())
978
+ }
979
+
980
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
981
+ t.Fatal(err.Error())
982
+ }
983
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "typeonlylocal", "a.gs.ts"))
984
+ if err != nil {
985
+ t.Fatal(err.Error())
986
+ }
987
+ text := string(content)
988
+ if !strings.Contains(text, "import type * as __goscript_payload from \"./payload.gs.ts\"") {
989
+ t.Fatalf("missing type-only same-package import:\n%s", text)
990
+ }
991
+ if strings.Contains(text, "import \"./payload.gs.ts\"") {
992
+ t.Fatalf("type-only same-package import should not force sibling module execution:\n%s", text)
993
+ }
994
+ }
995
+
996
+ func TestCompilePackagesPreservesSourceImportAliasesForAssociatedMethods(t *testing.T) {
997
+ moduleDir := writePackageGraphFixture(t, map[string]string{
998
+ "go.mod": "module example.test/sourcealiases\n\ngo 1.25.3\n",
999
+ "dep/block/errors.go": strings.Join([]string{
1000
+ "package block",
1001
+ "import \"errors\"",
1002
+ "var ErrUnexpectedType = errors.New(\"unexpected object type\")",
1003
+ "",
1004
+ }, "\n"),
1005
+ "dep/kvtx/block/store.go": strings.Join([]string{
1006
+ "package kvtx_block",
1007
+ "type KeyValueStore struct{}",
1008
+ "",
1009
+ }, "\n"),
1010
+ "world/world.go": strings.Join([]string{
1011
+ "package world",
1012
+ "import block \"example.test/sourcealiases/dep/kvtx/block\"",
1013
+ "type World struct {",
1014
+ " Object *block.KeyValueStore",
1015
+ "}",
1016
+ "",
1017
+ }, "\n"),
1018
+ "world/block-world.go": strings.Join([]string{
1019
+ "package world",
1020
+ "import (",
1021
+ " block \"example.test/sourcealiases/dep/block\"",
1022
+ " block_kvtx \"example.test/sourcealiases/dep/kvtx/block\"",
1023
+ ")",
1024
+ "func (w *World) Apply(next any) error {",
1025
+ " if _, ok := next.(*block_kvtx.KeyValueStore); !ok {",
1026
+ " return block.ErrUnexpectedType",
1027
+ " }",
1028
+ " return nil",
1029
+ "}",
1030
+ "",
1031
+ }, "\n"),
1032
+ })
1033
+ outputDir := filepath.Join(t.TempDir(), "output")
1034
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1035
+ if err != nil {
1036
+ t.Fatal(err.Error())
1037
+ }
1038
+
1039
+ if _, err := comp.CompilePackages(context.Background(), "./world"); err != nil {
1040
+ t.Fatal(err.Error())
1041
+ }
1042
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "sourcealiases", "world", "world.gs.ts")
1043
+ content, err := os.ReadFile(outputFile)
1044
+ if err != nil {
1045
+ t.Fatal(err.Error())
1046
+ }
1047
+ text := string(content)
1048
+ matches := regexp.MustCompile(`import \* as (\w+) from "@goscript/example\.test/sourcealiases/dep/block/index\.js"`).FindStringSubmatch(text)
1049
+ if len(matches) != 2 {
1050
+ t.Fatalf("missing dep/block import in generated output:\n%s", text)
1051
+ }
1052
+ if !strings.Contains(text, "return "+matches[1]+".ErrUnexpectedType") {
1053
+ t.Fatalf("selector did not use the alias for its source package %q:\n%s", matches[1], text)
1054
+ }
1055
+ }
1056
+
1057
+ func TestCompilePackagesEmitsSideEffectImportsForInterfaceRegistry(t *testing.T) {
1058
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1059
+ "go.mod": "module example.test/interface-registry\n\ngo 1.25.3\n",
1060
+ "main.go": strings.Join([]string{
1061
+ "package main",
1062
+ "import \"example.test/interface-registry/dep\"",
1063
+ "type localImpl struct{}",
1064
+ "func (*localImpl) Ping() string { return \"pong\" }",
1065
+ "func matchLocal(v any) bool {",
1066
+ " switch v.(type) {",
1067
+ " case Local:",
1068
+ " return true",
1069
+ " }",
1070
+ " return false",
1071
+ "}",
1072
+ "func matchDep(v any) bool {",
1073
+ " _, ok := v.(dep.Remote)",
1074
+ " return ok",
1075
+ "}",
1076
+ "func main() { println(matchLocal(&localImpl{}), matchDep(nil)) }",
1077
+ "",
1078
+ }, "\n"),
1079
+ "local.go": strings.Join([]string{
1080
+ "package main",
1081
+ "type Local interface { Ping() string }",
1082
+ "",
1083
+ }, "\n"),
1084
+ "dep/dep.go": strings.Join([]string{
1085
+ "package dep",
1086
+ "type Remote interface { Remote() string }",
1087
+ "",
1088
+ }, "\n"),
1089
+ })
1090
+ outputDir := filepath.Join(t.TempDir(), "output")
1091
+ comp, err := NewCompiler(&Config{
1092
+ Dir: moduleDir,
1093
+ OutputPath: outputDir,
1094
+ AllDependencies: true,
1095
+ }, nil, nil)
1096
+ if err != nil {
1097
+ t.Fatal(err.Error())
1098
+ }
1099
+
1100
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1101
+ t.Fatal(err.Error())
1102
+ }
1103
+ mainContent, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "interface-registry", "main.gs.ts"))
1104
+ if err != nil {
1105
+ t.Fatal(err.Error())
1106
+ }
1107
+ mainText := string(mainContent)
1108
+ for _, want := range []string{
1109
+ "import * as dep from \"@goscript/example.test/interface-registry/dep/index.js\"",
1110
+ "import \"@goscript/example.test/interface-registry/dep/index.js\"",
1111
+ "import type * as __goscript_local from \"./local.gs.ts\"",
1112
+ "case $.typeAssert<Exclude<__goscript_local.Local, null>>(__goscriptTypeSwitchValue, \"main.Local\").ok",
1113
+ "$.typeAssertTuple<dep.Remote | null>(v, \"dep.Remote\")",
1114
+ } {
1115
+ if !strings.Contains(mainText, want) {
1116
+ t.Fatalf("missing %q in main output:\n%s", want, mainText)
1117
+ }
1118
+ }
1119
+
1120
+ depIndexContent, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "interface-registry", "dep", "index.ts"))
1121
+ if err != nil {
1122
+ t.Fatal(err.Error())
1123
+ }
1124
+ if !strings.Contains(string(depIndexContent), "import \"./dep.gs.ts\"") {
1125
+ t.Fatalf("missing interface side-effect import in dep index:\n%s", string(depIndexContent))
1126
+ }
1127
+ }
1128
+
1129
+ func TestCompilePackagesEmitsIndexAddressRefs(t *testing.T) {
1130
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1131
+ "go.mod": "module example.test/indexaddr\n\ngo 1.25.3\n",
1132
+ "main.go": strings.Join([]string{
1133
+ "package main",
1134
+ "type Item struct { N int }",
1135
+ "func set(ptr *int, value int) {",
1136
+ " *ptr = value",
1137
+ "}",
1138
+ "func Use(values []int, i int) int {",
1139
+ " set(&values[i], 9)",
1140
+ " return values[i]",
1141
+ "}",
1142
+ "func Items() []*Item {",
1143
+ " return []*Item{{N: 1}}",
1144
+ "}",
1145
+ "",
1146
+ }, "\n"),
1147
+ })
1148
+ outputDir := filepath.Join(t.TempDir(), "output")
1149
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1150
+ if err != nil {
1151
+ t.Fatal(err.Error())
1152
+ }
1153
+
1154
+ _, err = comp.CompilePackages(context.Background(), ".")
1155
+ if err != nil {
1156
+ t.Fatal(err.Error())
1157
+ }
1158
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "indexaddr", "main.gs.ts")
1159
+ content, err := os.ReadFile(outputFile)
1160
+ if err != nil {
1161
+ t.Fatal(err.Error())
1162
+ }
1163
+ text := string(content)
1164
+ if !strings.Contains(text, "set($.indexRef(values!, i), 9)") {
1165
+ t.Fatalf("missing index address reference:\n%s", text)
1166
+ }
1167
+ if !strings.Contains(text, "new Item({N: 1})") {
1168
+ t.Fatalf("missing elided pointer composite literal:\n%s", text)
1169
+ }
1170
+ }
1171
+
1172
+ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
1173
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1174
+ "go.mod": "module example.test/structs\n\ngo 1.25.3\n",
1175
+ "main.go": strings.Join([]string{
1176
+ "package main",
1177
+ "type Counter struct {",
1178
+ " // Value counts reads.",
1179
+ " Value int `json:\"value\"`",
1180
+ " ID ObjectID",
1181
+ "}",
1182
+ "type ObjectID int32",
1183
+ "func (c Counter) Read() int {",
1184
+ " return c.Value",
1185
+ "}",
1186
+ "func (c *Counter) Set(v int) {",
1187
+ " c.Value = v",
1188
+ "}",
1189
+ "func NewCounter() *Counter {",
1190
+ " return &Counter{Value: 3}",
1191
+ "}",
1192
+ "func main() {",
1193
+ " original := Counter{Value: 1}",
1194
+ "",
1195
+ " // Copy should stay readable in generated output.",
1196
+ " copy := original",
1197
+ " pointer := &original",
1198
+ " pointer.Set(2)",
1199
+ " NewCounter().Set(5)",
1200
+ " var iface any = pointer",
1201
+ " _, ok := iface.(*Counter)",
1202
+ " println(copy.Read(), original.Read(), ok)",
1203
+ "}",
1204
+ "",
1205
+ }, "\n"),
1206
+ })
1207
+ outputDir := filepath.Join(t.TempDir(), "output")
1208
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1209
+ if err != nil {
1210
+ t.Fatal(err.Error())
1211
+ }
1212
+
1213
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1214
+ t.Fatal(err.Error())
1215
+ }
1216
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "structs", "main.gs.ts")
1217
+ content, err := os.ReadFile(outputFile)
1218
+ if err != nil {
1219
+ t.Fatal(err.Error())
1220
+ }
1221
+ text := string(content)
1222
+ for _, want := range []string{
1223
+ "export class Counter",
1224
+ "// Value counts reads.\n\tpublic get Value(): number",
1225
+ "public clone(): Counter",
1226
+ "public Read(): number",
1227
+ "public Set(v: number): void",
1228
+ "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))",
1229
+ "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))\n\n\t// Copy should stay readable in generated output.\n\tlet copy",
1230
+ "let copy = $.markAsStructValue($.cloneStructValue(original.value))",
1231
+ "let pointer: Counter | $.VarRef<Counter> | null = original",
1232
+ "Counter.prototype.Set.call(pointer, 2)",
1233
+ "Counter.prototype.Set.call(NewCounter(), 5)",
1234
+ "let [, ok] = $.typeAssertTuple<Counter | $.VarRef<Counter> | null>(iface, { kind: $.TypeKind.Pointer, elemType: \"main.Counter\" })",
1235
+ "\"Value\": { type: { kind: $.TypeKind.Basic, name: \"int\" }, tag: \"json:\\\"value\\\"\" }",
1236
+ "\"ID\": { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.ObjectID\" }",
1237
+ } {
1238
+ if !strings.Contains(text, want) {
1239
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1240
+ }
1241
+ }
1242
+ }
1243
+
1244
+ func TestCompilePackagesEscapesReservedTypeNames(t *testing.T) {
1245
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1246
+ "go.mod": "module example.test/reservedtypes\n\ngo 1.25.3\n",
1247
+ "main.go": strings.Join([]string{
1248
+ "package main",
1249
+ "type static struct {",
1250
+ " Value int",
1251
+ "}",
1252
+ "func newStatic() *static {",
1253
+ " return &static{Value: 7}",
1254
+ "}",
1255
+ "func (s *static) Read() int {",
1256
+ " return s.Value",
1257
+ "}",
1258
+ "func main() {",
1259
+ " println(newStatic().Read())",
1260
+ "}",
1261
+ "",
1262
+ }, "\n"),
1263
+ })
1264
+ outputDir := filepath.Join(t.TempDir(), "output")
1265
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1266
+ if err != nil {
1267
+ t.Fatal(err.Error())
1268
+ }
1269
+
1270
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1271
+ t.Fatal(err.Error())
1272
+ }
1273
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "reservedtypes", "main.gs.ts")
1274
+ content, err := os.ReadFile(outputFile)
1275
+ if err != nil {
1276
+ t.Fatal(err.Error())
1277
+ }
1278
+ text := string(content)
1279
+ for _, want := range []string{
1280
+ "export class _static",
1281
+ "new _static({Value: 7})",
1282
+ "public Read(): number",
1283
+ "_static.prototype.Read.call(newStatic())",
1284
+ "\"main.static\"",
1285
+ } {
1286
+ if !strings.Contains(text, want) {
1287
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1288
+ }
1289
+ }
1290
+ if strings.Contains(text, "class static") {
1291
+ t.Fatalf("reserved type name was not escaped:\n%s", text)
1292
+ }
1293
+ }
1294
+
1295
+ func TestCompilePackagesAvoidsPointerMethodTypeNameShadow(t *testing.T) {
1296
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1297
+ "go.mod": "module example.test/methodshadow\n\ngo 1.25.3\n",
1298
+ "main.go": strings.Join([]string{
1299
+ "package main",
1300
+ "type item struct {",
1301
+ " Value int",
1302
+ "}",
1303
+ "func (i *item) read() int {",
1304
+ " return i.Value",
1305
+ "}",
1306
+ "func total(items []*item) int {",
1307
+ " sum := 0",
1308
+ " for _, item := range items {",
1309
+ " sum += item.read()",
1310
+ " }",
1311
+ " return sum",
1312
+ "}",
1313
+ "",
1314
+ }, "\n"),
1315
+ })
1316
+ outputDir := filepath.Join(t.TempDir(), "output")
1317
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1318
+ if err != nil {
1319
+ t.Fatal(err.Error())
1320
+ }
1321
+
1322
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1323
+ t.Fatal(err.Error())
1324
+ }
1325
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "methodshadow", "main.gs.ts")
1326
+ content, err := os.ReadFile(outputFile)
1327
+ if err != nil {
1328
+ t.Fatal(err.Error())
1329
+ }
1330
+ text := string(content)
1331
+ if !strings.Contains(text, "$.pointerValue<item>(item).read()") {
1332
+ t.Fatalf("missing direct pointer method call for shadowed type name:\n%s", text)
1333
+ }
1334
+ if strings.Contains(text, "item.prototype.read.call(item") {
1335
+ t.Fatalf("shadowed local variable used as method class:\n%s", text)
1336
+ }
1337
+ }
1338
+
1339
+ func TestCompilePackagesErasesUnimportedTransitiveInterfaceField(t *testing.T) {
1340
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1341
+ "go.mod": "module example.test/transitivefield\n\ngo 1.25.3\n",
1342
+ "hash/hash.go": strings.Join([]string{
1343
+ "package hash",
1344
+ "type Hash interface {",
1345
+ " Write([]byte) (int, error)",
1346
+ "}",
1347
+ "",
1348
+ }, "\n"),
1349
+ "holder/holder.go": strings.Join([]string{
1350
+ "package holder",
1351
+ "import \"example.test/transitivefield/hash\"",
1352
+ "type Hasher struct {",
1353
+ " hash.Hash",
1354
+ "}",
1355
+ "",
1356
+ }, "\n"),
1357
+ "main.go": strings.Join([]string{
1358
+ "package main",
1359
+ "import \"example.test/transitivefield/holder\"",
1360
+ "func write(h holder.Hasher, p []byte) error {",
1361
+ " _, err := h.Hash.Write(p)",
1362
+ " return err",
1363
+ "}",
1364
+ "",
1365
+ }, "\n"),
1366
+ })
1367
+ outputDir := filepath.Join(t.TempDir(), "output")
1368
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1369
+ if err != nil {
1370
+ t.Fatal(err.Error())
1371
+ }
1372
+
1373
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1374
+ t.Fatal(err.Error())
1375
+ }
1376
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "transitivefield", "main.gs.ts")
1377
+ content, err := os.ReadFile(outputFile)
1378
+ if err != nil {
1379
+ t.Fatal(err.Error())
1380
+ }
1381
+ text := string(content)
1382
+ if !strings.Contains(text, "$.pointerValue<any>(h.Hash).Write(p)") {
1383
+ t.Fatalf("missing erased transitive interface field type:\n%s", text)
1384
+ }
1385
+ if strings.Contains(text, "Exclude<Hash") {
1386
+ t.Fatalf("unimported transitive interface type leaked into output:\n%s", text)
1387
+ }
1388
+ }
1389
+
1390
+ func TestCompilePackagesClonesNestedStructFieldsWithCloneMethodCollision(t *testing.T) {
1391
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1392
+ "go.mod": "module example.test/nestedclone\n\ngo 1.25.3\n",
1393
+ "main.go": strings.Join([]string{
1394
+ "package main",
1395
+ "type Box struct { Value int }",
1396
+ "func (b *Box) clone() (*Box, error) {",
1397
+ " return &Box{Value: b.Value + 1}, nil",
1398
+ "}",
1399
+ "type Holder struct { Box Box }",
1400
+ "func copyHolder(h Holder) Holder { return h }",
1401
+ "func main() {",
1402
+ " _ = copyHolder(Holder{Box: Box{Value: 1}})",
1403
+ "}",
1404
+ "",
1405
+ }, "\n"),
1406
+ })
1407
+ outputDir := filepath.Join(t.TempDir(), "output")
1408
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1409
+ if err != nil {
1410
+ t.Fatal(err.Error())
1411
+ }
1412
+
1413
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1414
+ t.Fatal(err.Error())
1415
+ }
1416
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "nestedclone", "main.gs.ts"))
1417
+ if err != nil {
1418
+ t.Fatal(err.Error())
1419
+ }
1420
+ text := string(content)
1421
+ for _, want := range []string{
1422
+ "public __goscriptClone(): Box",
1423
+ "Box: $.varRef(init?.Box ? $.markAsStructValue($.cloneStructValue(init.Box)) : $.markAsStructValue(new Box()))",
1424
+ "Box: $.varRef($.markAsStructValue($.cloneStructValue(this._fields.Box.value)))",
1425
+ } {
1426
+ if !strings.Contains(text, want) {
1427
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1428
+ }
1429
+ }
1430
+ if strings.Contains(text, "init.Box.clone()") || strings.Contains(text, "this._fields.Box.value.clone()") {
1431
+ t.Fatalf("nested struct-field clone bypassed cloneStructValue:\n%s", text)
1432
+ }
1433
+ }
1434
+
1435
+ func TestCompilePackagesEmitsNestedPointerStorageAssertions(t *testing.T) {
1436
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1437
+ "go.mod": "module example.test/pointers\n\ngo 1.25.3\n",
1438
+ "main.go": strings.Join([]string{
1439
+ "package main",
1440
+ "func main() {",
1441
+ " var x int = 10",
1442
+ " p1 := &x",
1443
+ " p2 := &p1",
1444
+ " p3 := &p2",
1445
+ " ***p3 = 12",
1446
+ " println(x)",
1447
+ "}",
1448
+ "",
1449
+ }, "\n"),
1450
+ })
1451
+ outputDir := filepath.Join(t.TempDir(), "output")
1452
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1453
+ if err != nil {
1454
+ t.Fatal(err.Error())
1455
+ }
1456
+
1457
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1458
+ t.Fatal(err.Error())
1459
+ }
1460
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "pointers", "main.gs.ts")
1461
+ content, err := os.ReadFile(outputFile)
1462
+ if err != nil {
1463
+ t.Fatal(err.Error())
1464
+ }
1465
+ text := string(content)
1466
+ for _, want := range []string{
1467
+ "let x: $.VarRef<number> = $.varRef(10)",
1468
+ "let p1 = $.varRef(x)",
1469
+ "let p2 = $.varRef(p1)",
1470
+ "let p3 = p2",
1471
+ "$.pointerValue<$.VarRef<number> | null>($.pointerValue<$.VarRef<$.VarRef<number> | null> | null>(p3))!.value = 12",
1472
+ } {
1473
+ if !strings.Contains(text, want) {
1474
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1475
+ }
1476
+ }
1477
+ }
1478
+
1479
+ func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
1480
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1481
+ "go.mod": "module example.test/collections\n\ngo 1.25.3\n",
1482
+ "main.go": strings.Join([]string{
1483
+ "package main",
1484
+ "type MyInt int",
1485
+ "func (m MyInt) Double() int { return int(m) * 2 }",
1486
+ "type MySlice []int",
1487
+ "func (s *MySlice) Add(v int) { *s = append(*s, v) }",
1488
+ "func main() {",
1489
+ " arr := [3]int{1: 10}",
1490
+ " slice := make([]int, 0, 2)",
1491
+ " empty := []rune{}",
1492
+ " literal := []int{1, 2}",
1493
+ " literal = append(literal, 3)",
1494
+ " slice = append(slice, 5)",
1495
+ " slice[0] = arr[1]",
1496
+ " m := make(map[string]int)",
1497
+ " m[\"one\"] = 1",
1498
+ " value, ok := m[\"missing\"]",
1499
+ " text := \"hé\"",
1500
+ " var list MySlice",
1501
+ " list.Add(7)",
1502
+ " println(arr[1], slice[0], literal[2], len(slice), cap(slice), len(empty), value, ok, text[0], text[1], MyInt(5).Double(), len(list))",
1503
+ "}",
1504
+ "",
1505
+ }, "\n"),
1506
+ })
1507
+ outputDir := filepath.Join(t.TempDir(), "output")
1508
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1509
+ if err != nil {
1510
+ t.Fatal(err.Error())
1511
+ }
1512
+
1513
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1514
+ t.Fatal(err.Error())
1515
+ }
1516
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "collections", "main.gs.ts")
1517
+ content, err := os.ReadFile(outputFile)
1518
+ if err != nil {
1519
+ t.Fatal(err.Error())
1520
+ }
1521
+ text := string(content)
1522
+ for _, want := range []string{
1523
+ "export type MyInt = number",
1524
+ "export function MyInt_Double(m: MyInt): number",
1525
+ "export type MySlice = $.Slice<number>",
1526
+ "export function MySlice_Add(s: $.VarRef<MySlice> | null, v: number): void",
1527
+ "let arr = [0, 10, 0]",
1528
+ "let slice: $.Slice<number> = $.makeSlice<number>(0, 2, \"number\")",
1529
+ "let empty: $.Slice<number> = $.arrayToSlice<number>([])",
1530
+ "let literal: $.Slice<number> = $.arrayToSlice<number>([1, 2])",
1531
+ "literal = $.append(literal, 3)",
1532
+ "slice![0] = arr[1]",
1533
+ "let m: Map<string, number> | null = $.makeMap<string, number>()",
1534
+ "$.mapSet(m, \"one\", 1)",
1535
+ "let [value, ok] = $.mapGet(m, \"missing\", 0)",
1536
+ "slice![0]",
1537
+ "literal![2]",
1538
+ "let list: $.VarRef<MySlice> = $.varRef(null as MySlice)",
1539
+ "MySlice_Add(list, 7)",
1540
+ "$.indexStringOrBytes(text, 0)",
1541
+ "MyInt_Double(5)",
1542
+ } {
1543
+ if !strings.Contains(text, want) {
1544
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1545
+ }
1546
+ }
1547
+ }
1548
+
1549
+ func TestCompilePackagesWrapsAddressedMapRangeValue(t *testing.T) {
1550
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1551
+ "go.mod": "module example.test/maprange\n\ngo 1.25.3\n",
1552
+ "main.go": strings.Join([]string{
1553
+ "package main",
1554
+ "type Item struct { Values []string }",
1555
+ "func (i *Item) add(v string) { i.Values = append(i.Values, v) }",
1556
+ "func Apply(m map[int]Item, values []string) {",
1557
+ " for k, item := range m {",
1558
+ " for _, v := range values {",
1559
+ " item.add(v)",
1560
+ " }",
1561
+ " m[k] = item",
1562
+ " }",
1563
+ "}",
1564
+ "",
1565
+ }, "\n"),
1566
+ })
1567
+ outputDir := filepath.Join(t.TempDir(), "output")
1568
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1569
+ if err != nil {
1570
+ t.Fatal(err.Error())
1571
+ }
1572
+
1573
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1574
+ t.Fatal(err.Error())
1575
+ }
1576
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "maprange", "main.gs.ts")
1577
+ content, err := os.ReadFile(outputFile)
1578
+ if err != nil {
1579
+ t.Fatal(err.Error())
1580
+ }
1581
+ text := string(content)
1582
+ for _, want := range []string{
1583
+ "let item: $.VarRef<Item> = $.varRef($.markAsStructValue($.cloneStructValue(__goscriptRangeValue",
1584
+ "item.value.add(v)",
1585
+ "$.mapSet(m, k, $.markAsStructValue($.cloneStructValue(item.value)))",
1586
+ } {
1587
+ if !strings.Contains(text, want) {
1588
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1589
+ }
1590
+ }
1591
+ }
1592
+
1593
+ func TestCompilePackagesLowersTupleAssignmentToMapIndexSet(t *testing.T) {
1594
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1595
+ "go.mod": "module example.test/maptupleassign\n\ngo 1.25.3\n",
1596
+ "main.go": strings.Join([]string{
1597
+ "package main",
1598
+ "func resolve(k string) (int, error) { return 7, nil }",
1599
+ "func Apply(keys []string) (map[string]int, error) {",
1600
+ " out := make(map[string]int)",
1601
+ " var err error",
1602
+ " for _, k := range keys {",
1603
+ " out[k], err = resolve(k)",
1604
+ " if err != nil { return nil, err }",
1605
+ " }",
1606
+ " return out, nil",
1607
+ "}",
1608
+ "",
1609
+ }, "\n"),
1610
+ })
1611
+ outputDir := filepath.Join(t.TempDir(), "output")
1612
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1613
+ if err != nil {
1614
+ t.Fatal(err.Error())
1615
+ }
1616
+
1617
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1618
+ t.Fatal(err.Error())
1619
+ }
1620
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "maptupleassign", "main.gs.ts")
1621
+ content, err := os.ReadFile(outputFile)
1622
+ if err != nil {
1623
+ t.Fatal(err.Error())
1624
+ }
1625
+ text := string(content)
1626
+ if !strings.Contains(text, "$.mapSet(out, k, __goscriptTuple") {
1627
+ t.Fatalf("tuple assignment to map index did not write through mapSet:\n%s", text)
1628
+ }
1629
+ if strings.Contains(text, "$.mapGet(out, k, 0)[0] =") {
1630
+ t.Fatalf("tuple assignment still mutates mapGet tuple instead of map:\n%s", text)
1631
+ }
1632
+ }
1633
+
1634
+ func TestCompilePackagesLowersPromotedNamedPrimitiveMethod(t *testing.T) {
1635
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1636
+ "go.mod": "module example.test/promotedprimitive\n\ngo 1.25.3\n",
1637
+ "main.go": strings.Join([]string{
1638
+ "package main",
1639
+ "import \"time\"",
1640
+ "type Lifetime struct { time.Duration }",
1641
+ "func Seconds(l Lifetime) float64 {",
1642
+ " return l.Seconds()",
1643
+ "}",
1644
+ "",
1645
+ }, "\n"),
1646
+ })
1647
+ outputDir := filepath.Join(t.TempDir(), "output")
1648
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1649
+ if err != nil {
1650
+ t.Fatal(err.Error())
1651
+ }
1652
+
1653
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1654
+ t.Fatal(err.Error())
1655
+ }
1656
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "promotedprimitive", "main.gs.ts")
1657
+ content, err := os.ReadFile(outputFile)
1658
+ if err != nil {
1659
+ t.Fatal(err.Error())
1660
+ }
1661
+ text := string(content)
1662
+ if !strings.Contains(text, "return time.Duration_Seconds(l.Duration)") {
1663
+ t.Fatalf("promoted named primitive method was not lowered through the owner function:\n%s", text)
1664
+ }
1665
+ if strings.Contains(text, ".Seconds()") {
1666
+ t.Fatalf("promoted named primitive method still used a JavaScript member call:\n%s", text)
1667
+ }
1668
+ }
1669
+
1670
+ func TestCompilePackagesMarksPackageFunctionVariablesAsync(t *testing.T) {
1671
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1672
+ "go.mod": "module example.test/packagefuncvar\n\ngo 1.25.3\n",
1673
+ "dep/dep.go": strings.Join([]string{
1674
+ "package dep",
1675
+ "func parse(raw string) (int, error) { return len(raw), nil }",
1676
+ "var Parse = parse",
1677
+ "",
1678
+ }, "\n"),
1679
+ "main.go": strings.Join([]string{
1680
+ "package main",
1681
+ "import \"example.test/packagefuncvar/dep\"",
1682
+ "func parse() (int, error) {",
1683
+ " return dep.Parse(\"turn\")",
1684
+ "}",
1685
+ "",
1686
+ }, "\n"),
1687
+ })
1688
+ outputDir := filepath.Join(t.TempDir(), "output")
1689
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1690
+ if err != nil {
1691
+ t.Fatal(err.Error())
1692
+ }
1693
+
1694
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1695
+ t.Fatal(err.Error())
1696
+ }
1697
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "packagefuncvar", "main.gs.ts")
1698
+ content, err := os.ReadFile(outputFile)
1699
+ if err != nil {
1700
+ t.Fatal(err.Error())
1701
+ }
1702
+ text := string(content)
1703
+ if !strings.Contains(text, "export async function parse(): globalThis.Promise<[number, $.GoError]>") {
1704
+ t.Fatalf("package function variable caller was not marked async:\n%s", text)
1705
+ }
1706
+ if !strings.Contains(text, "return await dep.Parse!(\"turn\")") {
1707
+ t.Fatalf("package function variable call was not awaited:\n%s", text)
1708
+ }
1709
+ }
1710
+
1711
+ func TestCompilePackagesAwaitsImportedAsyncMethodsAndFunctions(t *testing.T) {
1712
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1713
+ "go.mod": "module example.test/importedasync\n\ngo 1.25.3\n",
1714
+ "dep/dep.go": strings.Join([]string{
1715
+ "package dep",
1716
+ "type Host struct{ ch chan struct{} }",
1717
+ "func (h *Host) Snapshot() (int, error) { <-h.ch; return 1, nil }",
1718
+ "func Read(h *Host) (int, error) { return h.Snapshot() }",
1719
+ "",
1720
+ }, "\n"),
1721
+ "dep/dep_test.go": strings.Join([]string{
1722
+ "package dep",
1723
+ "import \"testing\"",
1724
+ "func TestHost(t *testing.T) {}",
1725
+ "",
1726
+ }, "\n"),
1727
+ "main.go": strings.Join([]string{
1728
+ "package main",
1729
+ "import \"example.test/importedasync/dep\"",
1730
+ "func UseMethod(h *dep.Host) (int, error) {",
1731
+ " return h.Snapshot()",
1732
+ "}",
1733
+ "func UseFunction(h *dep.Host) (int, error) {",
1734
+ " return dep.Read(h)",
1735
+ "}",
1736
+ "",
1737
+ }, "\n"),
1738
+ "main_test.go": strings.Join([]string{
1739
+ "package main",
1740
+ "import \"testing\"",
1741
+ "func TestUse(t *testing.T) {}",
1742
+ "",
1743
+ }, "\n"),
1744
+ })
1745
+ outputDir := filepath.Join(t.TempDir(), "output")
1746
+ service := NewCompileService()
1747
+ _, err := service.Compile(context.Background(), &CompileRequest{
1748
+ Patterns: []string{".", "./dep"},
1749
+ Dir: moduleDir,
1750
+ OutputPath: outputDir,
1751
+ DependencyMode: DependencyModeAll,
1752
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
1753
+ Tests: true,
1754
+ AllDependencies: true,
1755
+ })
1756
+ if err != nil {
1757
+ t.Fatal(err.Error())
1758
+ }
1759
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "importedasync", "main.gs.ts")
1760
+ content, err := os.ReadFile(outputFile)
1761
+ if err != nil {
1762
+ t.Fatal(err.Error())
1763
+ }
1764
+ text := string(content)
1765
+ if !strings.Contains(text, "export async function UseMethod") {
1766
+ t.Fatalf("imported async method caller was not marked async:\n%s", text)
1767
+ }
1768
+ if !strings.Contains(text, "return await dep.Host.prototype.Snapshot.call(h)") {
1769
+ t.Fatalf("imported async method call was not awaited:\n%s", text)
1770
+ }
1771
+ if !strings.Contains(text, "export async function UseFunction") {
1772
+ t.Fatalf("imported async function caller was not marked async:\n%s", text)
1773
+ }
1774
+ if !strings.Contains(text, "return await dep.Read(h)") {
1775
+ t.Fatalf("imported async function call was not awaited:\n%s", text)
1776
+ }
1777
+ }
1778
+
1779
+ func TestCompilePackagesCastsConvertedTupleCallSpreads(t *testing.T) {
1780
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1781
+ "go.mod": "module example.test/tuplecallspread\n\ngo 1.25.3\n",
1782
+ "main.go": strings.Join([]string{
1783
+ "package main",
1784
+ "type Resolver interface { Resolve() }",
1785
+ "type relay struct{}",
1786
+ "func (*relay) Resolve() {}",
1787
+ "func NewRelay() (*relay, error) { return &relay{}, nil }",
1788
+ "func R(res Resolver, err error) ([]Resolver, error) {",
1789
+ " if err != nil { return nil, err }",
1790
+ " return []Resolver{res}, nil",
1791
+ "}",
1792
+ "func Use() ([]Resolver, error) {",
1793
+ " return R(NewRelay())",
1794
+ "}",
1795
+ "",
1796
+ }, "\n"),
1797
+ })
1798
+ outputDir := filepath.Join(t.TempDir(), "output")
1799
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1800
+ if err != nil {
1801
+ t.Fatal(err.Error())
1802
+ }
1803
+
1804
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1805
+ t.Fatal(err.Error())
1806
+ }
1807
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "tuplecallspread", "main.gs.ts")
1808
+ content, err := os.ReadFile(outputFile)
1809
+ if err != nil {
1810
+ t.Fatal(err.Error())
1811
+ }
1812
+ text := string(content)
1813
+ if !strings.Contains(text, "return R(...(() => {") ||
1814
+ !strings.Contains(text, "] as [Resolver | null, $.GoError]") {
1815
+ t.Fatalf("converted tuple call spread was not cast as a TypeScript tuple:\n%s", text)
484
1816
  }
485
1817
  }
486
1818
 
487
- func TestCompilePackagesEmitsIndexAddressRefs(t *testing.T) {
1819
+ func TestCompilePackagesPreservesBlankNamedResultSlots(t *testing.T) {
488
1820
  moduleDir := writePackageGraphFixture(t, map[string]string{
489
- "go.mod": "module example.test/indexaddr\n\ngo 1.25.3\n",
1821
+ "go.mod": "module example.test/blankresult\n\ngo 1.25.3\n",
490
1822
  "main.go": strings.Join([]string{
491
1823
  "package main",
492
- "type Item struct { N int }",
493
- "func set(ptr *int, value int) {",
494
- " *ptr = value",
495
- "}",
496
- "func Use(values []int, i int) int {",
497
- " set(&values[i], 9)",
498
- " return values[i]",
499
- "}",
500
- "func Items() []*Item {",
501
- " return []*Item{{N: 1}}",
1824
+ "func values() (first, second bool, _ error) {",
1825
+ " first = true",
1826
+ " return",
502
1827
  "}",
503
1828
  "",
504
1829
  }, "\n"),
@@ -509,53 +1834,37 @@ func TestCompilePackagesEmitsIndexAddressRefs(t *testing.T) {
509
1834
  t.Fatal(err.Error())
510
1835
  }
511
1836
 
512
- _, err = comp.CompilePackages(context.Background(), ".")
513
- if err != nil {
1837
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
514
1838
  t.Fatal(err.Error())
515
1839
  }
516
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "indexaddr", "main.gs.ts")
1840
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "blankresult", "main.gs.ts")
517
1841
  content, err := os.ReadFile(outputFile)
518
1842
  if err != nil {
519
1843
  t.Fatal(err.Error())
520
1844
  }
521
1845
  text := string(content)
522
- if !strings.Contains(text, "set($.indexRef(values!, i), 9)") {
523
- t.Fatalf("missing index address reference:\n%s", text)
524
- }
525
- if !strings.Contains(text, "new Item({N: 1})") {
526
- t.Fatalf("missing elided pointer composite literal:\n%s", text)
1846
+ if !strings.Contains(text, "return [first, second, null as $.GoError]") {
1847
+ t.Fatalf("blank named result slot was not preserved in naked return:\n%s", text)
527
1848
  }
528
1849
  }
529
1850
 
530
- func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
1851
+ func TestCompilePackagesCastsGenericMethodResultAssignments(t *testing.T) {
531
1852
  moduleDir := writePackageGraphFixture(t, map[string]string{
532
- "go.mod": "module example.test/structs\n\ngo 1.25.3\n",
1853
+ "go.mod": "module example.test/genericmethodresult\n\ngo 1.25.3\n",
533
1854
  "main.go": strings.Join([]string{
534
1855
  "package main",
535
- "type Counter struct {",
536
- " // Value counts reads.",
537
- " Value int `json:\"value\"`",
538
- "}",
539
- "func (c Counter) Read() int {",
540
- " return c.Value",
541
- "}",
542
- "func (c *Counter) Set(v int) {",
543
- " c.Value = v",
544
- "}",
545
- "func NewCounter() *Counter {",
546
- " return &Counter{Value: 3}",
547
- "}",
548
- "func main() {",
549
- " original := Counter{Value: 1}",
550
- "",
551
- " // Copy should stay readable in generated output.",
552
- " copy := original",
553
- " pointer := &original",
554
- " pointer.Set(2)",
555
- " NewCounter().Set(5)",
556
- " var iface any = pointer",
557
- " _, ok := iface.(*Counter)",
558
- " println(copy.Read(), original.Read(), ok)",
1856
+ "import (",
1857
+ " \"errors\"",
1858
+ " \"sync/atomic\"",
1859
+ ")",
1860
+ "func load() error {",
1861
+ " var ptr atomic.Pointer[error]",
1862
+ " err := errors.New(\"boom\")",
1863
+ " ptr.Store(&err)",
1864
+ " if errp := ptr.Load(); errp != nil {",
1865
+ " return *errp",
1866
+ " }",
1867
+ " return nil",
559
1868
  "}",
560
1869
  "",
561
1870
  }, "\n"),
@@ -569,45 +1878,25 @@ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
569
1878
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
570
1879
  t.Fatal(err.Error())
571
1880
  }
572
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "structs", "main.gs.ts")
1881
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "genericmethodresult", "main.gs.ts")
573
1882
  content, err := os.ReadFile(outputFile)
574
1883
  if err != nil {
575
1884
  t.Fatal(err.Error())
576
1885
  }
577
1886
  text := string(content)
578
- for _, want := range []string{
579
- "export class Counter",
580
- "// Value counts reads.\n\tpublic get Value(): number",
581
- "public clone(): Counter",
582
- "public Read(): number",
583
- "public Set(v: number): void",
584
- "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))",
585
- "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))\n\n\t// Copy should stay readable in generated output.\n\tlet copy",
586
- "let copy = $.markAsStructValue($.cloneStructValue(original.value))",
587
- "let pointer: Counter | $.VarRef<Counter> | null = original",
588
- "$.pointerValue<Counter>(pointer).Set(2)",
589
- "$.pointerValue<Counter>(NewCounter()).Set(5)",
590
- "let [, ok] = $.typeAssertTuple<Counter | $.VarRef<Counter> | null>(iface, { kind: $.TypeKind.Pointer, elemType: \"main.Counter\" })",
591
- "\"Value\": { type: { kind: $.TypeKind.Basic, name: \"int\" }, tag: \"json:\\\"value\\\"\" }",
592
- } {
593
- if !strings.Contains(text, want) {
594
- t.Fatalf("missing %q in generated output:\n%s", want, text)
595
- }
1887
+ if !strings.Contains(text, "(ptr.value.Load() as $.VarRef<$.GoError> | null)") {
1888
+ t.Fatalf("generic method result assignment was not cast to the target type:\n%s", text)
596
1889
  }
597
1890
  }
598
1891
 
599
- func TestCompilePackagesEmitsNestedPointerStorageAssertions(t *testing.T) {
1892
+ func TestCompilePackagesPreservesFloatConversionLiterals(t *testing.T) {
600
1893
  moduleDir := writePackageGraphFixture(t, map[string]string{
601
- "go.mod": "module example.test/pointers\n\ngo 1.25.3\n",
1894
+ "go.mod": "module example.test/floatconv\n\ngo 1.25.3\n",
602
1895
  "main.go": strings.Join([]string{
603
1896
  "package main",
604
- "func main() {",
605
- " var x int = 10",
606
- " p1 := &x",
607
- " p2 := &p1",
608
- " p3 := &p2",
609
- " ***p3 = 12",
610
- " println(x)",
1897
+ "import \"math\"",
1898
+ "func value() float64 {",
1899
+ " return math.Pow(float64(0.69314718056), 2)",
611
1900
  "}",
612
1901
  "",
613
1902
  }, "\n"),
@@ -621,49 +1910,30 @@ func TestCompilePackagesEmitsNestedPointerStorageAssertions(t *testing.T) {
621
1910
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
622
1911
  t.Fatal(err.Error())
623
1912
  }
624
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "pointers", "main.gs.ts")
1913
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "floatconv", "main.gs.ts")
625
1914
  content, err := os.ReadFile(outputFile)
626
1915
  if err != nil {
627
1916
  t.Fatal(err.Error())
628
1917
  }
629
1918
  text := string(content)
630
- for _, want := range []string{
631
- "let x: $.VarRef<number> = $.varRef(10)",
632
- "let p1 = $.varRef(x)",
633
- "let p2 = $.varRef(p1)",
634
- "let p3 = p2",
635
- "$.pointerValue<$.VarRef<number> | null>($.pointerValue<$.VarRef<$.VarRef<number> | null> | null>(p3))!.value = 12",
636
- } {
637
- if !strings.Contains(text, want) {
638
- t.Fatalf("missing %q in generated output:\n%s", want, text)
639
- }
1919
+ if strings.Contains(text, "$.int(0.69314718056)") {
1920
+ t.Fatalf("float64 literal conversion was truncated through $.int:\n%s", text)
1921
+ }
1922
+ if !strings.Contains(text, "math.Pow(0.69314718056, 2)") {
1923
+ t.Fatalf("missing direct float literal conversion:\n%s", text)
640
1924
  }
641
1925
  }
642
1926
 
643
- func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
1927
+ func TestCompilePackagesLowersStringOrderingThroughRuntime(t *testing.T) {
644
1928
  moduleDir := writePackageGraphFixture(t, map[string]string{
645
- "go.mod": "module example.test/collections\n\ngo 1.25.3\n",
1929
+ "go.mod": "module example.test/stringorder\n\ngo 1.25.3\n",
646
1930
  "main.go": strings.Join([]string{
647
1931
  "package main",
648
- "type MyInt int",
649
- "func (m MyInt) Double() int { return int(m) * 2 }",
650
- "type MySlice []int",
651
- "func (s *MySlice) Add(v int) { *s = append(*s, v) }",
652
- "func main() {",
653
- " arr := [3]int{1: 10}",
654
- " slice := make([]int, 0, 2)",
655
- " empty := []rune{}",
656
- " literal := []int{1, 2}",
657
- " literal = append(literal, 3)",
658
- " slice = append(slice, 5)",
659
- " slice[0] = arr[1]",
660
- " m := make(map[string]int)",
661
- " m[\"one\"] = 1",
662
- " value, ok := m[\"missing\"]",
663
- " text := \"hé\"",
664
- " var list MySlice",
665
- " list.Add(7)",
666
- " println(arr[1], slice[0], literal[2], len(slice), cap(slice), len(empty), value, ok, text[0], text[1], MyInt(5).Double(), len(list))",
1932
+ "func less(left, right []byte) bool {",
1933
+ " return string(left) < string(right)",
1934
+ "}",
1935
+ "func ordered(left, right string) bool {",
1936
+ " return left <= right",
667
1937
  "}",
668
1938
  "",
669
1939
  }, "\n"),
@@ -677,32 +1947,15 @@ func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
677
1947
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
678
1948
  t.Fatal(err.Error())
679
1949
  }
680
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "collections", "main.gs.ts")
1950
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "stringorder", "main.gs.ts")
681
1951
  content, err := os.ReadFile(outputFile)
682
1952
  if err != nil {
683
1953
  t.Fatal(err.Error())
684
1954
  }
685
1955
  text := string(content)
686
1956
  for _, want := range []string{
687
- "export type MyInt = number",
688
- "export function MyInt_Double(m: MyInt): number",
689
- "export type MySlice = $.Slice<number>",
690
- "export function MySlice_Add(s: $.VarRef<MySlice> | null, v: number): void",
691
- "let arr = [0, 10, 0]",
692
- "let slice = $.makeSlice<number>(0, 2, \"number\")",
693
- "let empty = $.arrayToSlice<number>([])",
694
- "let literal = $.arrayToSlice<number>([1, 2])",
695
- "literal = $.append(literal, 3)",
696
- "slice![0] = arr[1]",
697
- "let m: Map<string, number> | null = $.makeMap<string, number>()",
698
- "$.mapSet(m, \"one\", 1)",
699
- "let [value, ok] = $.mapGet(m, \"missing\", 0)",
700
- "slice![0]",
701
- "literal![2]",
702
- "let list: $.VarRef<MySlice> = $.varRef(null as MySlice)",
703
- "MySlice_Add(list, 7)",
704
- "$.indexStringOrBytes(text, 0)",
705
- "MyInt_Double(5)",
1957
+ "$.stringCompare(left, right) < 0",
1958
+ "$.stringCompare(left, right) <= 0",
706
1959
  } {
707
1960
  if !strings.Contains(text, want) {
708
1961
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -759,18 +2012,18 @@ func TestCompilePackagesEmitsInterfacesMethodValuesTypeSwitchesAndFunctionAssert
759
2012
  text := string(content)
760
2013
  for _, want := range []string{
761
2014
  "export type Greeter = ((name: string) => string | globalThis.Promise<string>) | null",
762
- "export type ReadCloser = null | {",
2015
+ "export type ReadCloser = {",
763
2016
  "Read(): string",
764
2017
  "Close(): string",
765
2018
  "$.registerInterfaceType(\n\t\"main.ReadCloser\"",
766
2019
  "((__receiver) => () => __receiver.Inc())($.pointerValue<Counter>(counter))",
767
2020
  "$.namedFunction(greet, \"main.Greeter\")",
768
- "$.typedNil(\"*struct{Name string}\")",
2021
+ "$.interfaceValue<any>(null, \"*struct{Name string}\")",
769
2022
  "elemType: { kind: $.TypeKind.Struct, methods: [], fields: {\"Name\": { kind: $.TypeKind.Basic, name: \"string\" }} }",
770
2023
  "let fn = __goscriptTuple",
771
2024
  "switch (true)",
772
- "case $.typeAssert<ReadCloser | null>(__goscriptTypeSwitchValue, \"main.ReadCloser\").ok",
773
- "let v: ReadCloser | null = $.typeAssert<ReadCloser | null>(__goscriptTypeSwitchValue, \"main.ReadCloser\").value",
2025
+ "case $.typeAssert<Exclude<ReadCloser, null>>(__goscriptTypeSwitchValue, \"main.ReadCloser\").ok",
2026
+ "let v: Exclude<ReadCloser, null> = $.typeAssert<Exclude<ReadCloser, null>>(__goscriptTypeSwitchValue, \"main.ReadCloser\").value",
774
2027
  } {
775
2028
  if !strings.Contains(text, want) {
776
2029
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -812,7 +2065,7 @@ func TestCompilePackagesAssertsInterfaceMethodReceivers(t *testing.T) {
812
2065
  }
813
2066
  text := string(content)
814
2067
  for _, want := range []string{
815
- "export type FileInfo = null | {",
2068
+ "export type FileInfo = {",
816
2069
  "$.println($.pointerValue<Exclude<FileInfo, null>>(info).Name())",
817
2070
  } {
818
2071
  if !strings.Contains(text, want) {
@@ -821,6 +2074,58 @@ func TestCompilePackagesAssertsInterfaceMethodReceivers(t *testing.T) {
821
2074
  }
822
2075
  }
823
2076
 
2077
+ func TestCompilePackagesUsesNonNilInterfaceTypeSwitchCaseVarRefs(t *testing.T) {
2078
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2079
+ "go.mod": "module example.test/interface-switch-varref\n\ngo 1.25.3\n",
2080
+ "main.go": strings.Join([]string{
2081
+ "package main",
2082
+ "type rawConn interface { Control() error }",
2083
+ "type tcpConn interface {",
2084
+ " SyscallConn() (rawConn, error)",
2085
+ " SetLinger(int) error",
2086
+ "}",
2087
+ "type Conn struct { c rawConn }",
2088
+ "func keep(*tcpConn) {}",
2089
+ "func NewConn(c any) (*Conn, error) {",
2090
+ " cc := &Conn{}",
2091
+ " var err error",
2092
+ " switch c := c.(type) {",
2093
+ " case tcpConn:",
2094
+ " keep(&c)",
2095
+ " cc.c, err = c.SyscallConn()",
2096
+ " }",
2097
+ " return cc, err",
2098
+ "}",
2099
+ "",
2100
+ }, "\n"),
2101
+ })
2102
+ outputDir := filepath.Join(t.TempDir(), "output")
2103
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2104
+ if err != nil {
2105
+ t.Fatal(err.Error())
2106
+ }
2107
+
2108
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2109
+ t.Fatal(err.Error())
2110
+ }
2111
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "interface-switch-varref", "main.gs.ts")
2112
+ content, err := os.ReadFile(outputFile)
2113
+ if err != nil {
2114
+ t.Fatal(err.Error())
2115
+ }
2116
+ text := string(content)
2117
+ for _, want := range []string{
2118
+ "export type tcpConn = {",
2119
+ "case $.typeAssert<Exclude<tcpConn, null>>(__goscriptTypeSwitchValue, \"main.tcpConn\").ok",
2120
+ "let c: $.VarRef<Exclude<tcpConn, null>> = $.varRef($.typeAssert<Exclude<tcpConn, null>>(__goscriptTypeSwitchValue, \"main.tcpConn\").value)",
2121
+ "$.pointerValue<Exclude<tcpConn, null>>(c.value).SyscallConn()",
2122
+ } {
2123
+ if !strings.Contains(text, want) {
2124
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2125
+ }
2126
+ }
2127
+ }
2128
+
824
2129
  func TestCompilePackagesBoxesTypedNilInterfaceValues(t *testing.T) {
825
2130
  moduleDir := writePackageGraphFixture(t, map[string]string{
826
2131
  "go.mod": "module example.test/typed-nil-interface\n\ngo 1.25.3\n",
@@ -872,6 +2177,48 @@ func TestCompilePackagesBoxesTypedNilInterfaceValues(t *testing.T) {
872
2177
  }
873
2178
  }
874
2179
 
2180
+ func TestCompilePackagesBoxesAliasPointerInterfacesWithTargetRuntimeType(t *testing.T) {
2181
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2182
+ "go.mod": "module example.test/alias-interface-box\n\ngo 1.25.3\n",
2183
+ "dep/dep.go": strings.Join([]string{
2184
+ "package dep",
2185
+ "type CloudError struct{}",
2186
+ "func (*CloudError) Error() string { return \"cloud\" }",
2187
+ "",
2188
+ }, "\n"),
2189
+ "main.go": strings.Join([]string{
2190
+ "package main",
2191
+ "import \"example.test/alias-interface-box/dep\"",
2192
+ "type cloudError = dep.CloudError",
2193
+ "func Build() error {",
2194
+ " return &cloudError{}",
2195
+ "}",
2196
+ "",
2197
+ }, "\n"),
2198
+ })
2199
+ outputDir := filepath.Join(t.TempDir(), "output")
2200
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2201
+ if err != nil {
2202
+ t.Fatal(err.Error())
2203
+ }
2204
+
2205
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2206
+ t.Fatal(err.Error())
2207
+ }
2208
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "alias-interface-box", "main.gs.ts")
2209
+ content, err := os.ReadFile(outputFile)
2210
+ if err != nil {
2211
+ t.Fatal(err.Error())
2212
+ }
2213
+ text := string(content)
2214
+ if !strings.Contains(text, "$.interfaceValue<$.GoError>(new dep.CloudError(), \"*dep.CloudError\")") {
2215
+ t.Fatalf("alias pointer was not boxed with target runtime type:\n%s", text)
2216
+ }
2217
+ if strings.Contains(text, "*main.cloudError") {
2218
+ t.Fatalf("alias pointer leaked alias runtime type:\n%s", text)
2219
+ }
2220
+ }
2221
+
875
2222
  func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T) {
876
2223
  moduleDir := writePackageGraphFixture(t, map[string]string{
877
2224
  "go.mod": "module example.test/generics\n\ngo 1.25.3\n",
@@ -893,6 +2240,9 @@ func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T)
893
2240
  " var zero T",
894
2241
  " return zero",
895
2242
  "}",
2243
+ "func Copy[T any](vals ...T) []T {",
2244
+ " return append([]T{}, vals...)",
2245
+ "}",
896
2246
  "func main() {",
897
2247
  " box := NewBox(7)",
898
2248
  " println(box.Get())",
@@ -924,13 +2274,18 @@ func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T)
924
2274
  for _, want := range []string{
925
2275
  "public Get(): any",
926
2276
  "export function NewBox(__typeArgs: $.GenericTypeArgs | undefined, value: any): Box",
2277
+ "export function ZeroValue(__typeArgs: $.GenericTypeArgs | undefined): any",
2278
+ "export function CallString(__typeArgs: $.GenericTypeArgs | undefined, v: any): string",
2279
+ "export function Sum<T>(__typeArgs: $.GenericTypeArgs | undefined, vals: $.Slice<T>): any",
2280
+ "export function Copy<T>(__typeArgs: $.GenericTypeArgs | undefined, vals: $.Slice<T>): $.Slice<T>",
2281
+ "return $.append($.arrayToSlice<T>([]), ...(vals ?? []))",
927
2282
  "let seen: Set = $.makeMap<number, {}>()",
928
2283
  "$.mapSet(seen, 1, {})",
929
2284
  "$.genericZero(__typeArgs, \"T\", null)",
930
2285
  "$.callGenericMethod(__typeArgs, \"T\", \"String\", v)",
931
- "ZeroValue({T: { type: \"main.MyInt\", zero: () => 0, methods: {String: MyInt_String} }})",
932
- "CallString({T: { type: \"main.MyInt\", zero: () => 0, methods: {String: MyInt_String} }}, zero)",
933
- "Sum({T: { type: \"main.MyInt\", zero: () => 0, methods: {String: MyInt_String} }}, null)",
2286
+ "ZeroValue({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)} }})",
2287
+ "CallString({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)} }}, zero)",
2288
+ "Sum({T: { type: { kind: $.TypeKind.Basic, name: \"int\", typeName: \"main.MyInt\" }, zero: () => 0, methods: {String: (receiver: any, ...args: any[]) => (MyInt_String as any)(($.isVarRef(receiver) ? receiver.value : receiver), ...args)} }}, null)",
934
2289
  } {
935
2290
  if !strings.Contains(text, want) {
936
2291
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -1015,8 +2370,8 @@ func TestCompilePackagesEmitsRecursiveFunctionTypeInfo(t *testing.T) {
1015
2370
  }
1016
2371
  text := string(content)
1017
2372
  for _, want := range []string{
1018
- "export type Handler = ((_p0: ((_p0: Handler) => Handler | globalThis.Promise<Handler>) | null) => Handler | globalThis.Promise<Handler>) | null",
1019
- "\"Next\": { kind: $.TypeKind.Function, name: \"main.Handler\"",
2373
+ "export type Handler = ((_p0: ((_p0: Handler | null) => Handler | null | globalThis.Promise<Handler | null>) | null) => Handler | null | globalThis.Promise<Handler | null>) | null",
2374
+ "\"Next\": ({ kind: $.TypeKind.Function, name: \"main.Handler\"",
1020
2375
  "params: [{ kind: $.TypeKind.Function, params: [], results: [] }]",
1021
2376
  "results: [{ kind: $.TypeKind.Function, params: [], results: [] }]",
1022
2377
  } {
@@ -1082,7 +2437,7 @@ func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
1082
2437
  "export type Collector = ((label: string, parts: $.Slice<string>) => string | globalThis.Promise<string>) | null",
1083
2438
  "Join(parts: $.Slice<string>): string",
1084
2439
  "export function collect(label: string, parts: $.Slice<string>): string",
1085
- "let part = parts![__rangeIndex]",
2440
+ "let part = __goscriptRangeTarget0![__rangeIndex]",
1086
2441
  "export function maybeErr(parts: $.Slice<string>): $.GoError",
1087
2442
  "public Join(parts: $.Slice<string>): string",
1088
2443
  "collect(\"none\", null)",
@@ -1099,43 +2454,149 @@ func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
1099
2454
  }
1100
2455
  }
1101
2456
 
1102
- func TestCompilePackagesPacksVariadicCallsInGeneratedSubpackage(t *testing.T) {
2457
+ func TestCompilePackagesPacksVariadicCallsInGeneratedSubpackage(t *testing.T) {
2458
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2459
+ "go.mod": "module example.test/variadic-subpackage\n\ngo 1.25.3\n",
2460
+ "json/json.go": strings.Join([]string{
2461
+ "package json",
2462
+ "import \"fmt\"",
2463
+ "type State struct { err error }",
2464
+ "func (s *State) SetErrorf(format string, a ...any) {",
2465
+ " s.err = fmt.Errorf(format, a...)",
2466
+ "}",
2467
+ "func (s *State) Read(key string) {",
2468
+ " s.SetErrorf(\"bad %q\", key)",
2469
+ "}",
2470
+ "",
2471
+ }, "\n"),
2472
+ })
2473
+ outputDir := filepath.Join(t.TempDir(), "output")
2474
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2475
+ if err != nil {
2476
+ t.Fatal(err.Error())
2477
+ }
2478
+
2479
+ if _, err := comp.CompilePackages(context.Background(), "./json"); err != nil {
2480
+ t.Fatal(err.Error())
2481
+ }
2482
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "variadic-subpackage", "json", "json.gs.ts")
2483
+ content, err := os.ReadFile(outputFile)
2484
+ if err != nil {
2485
+ t.Fatal(err.Error())
2486
+ }
2487
+ text := string(content)
2488
+ want := "State.prototype.SetErrorf.call(s, \"bad %q\", $.arrayToSlice<any>([key]))"
2489
+ if !strings.Contains(text, want) {
2490
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2491
+ }
2492
+ if strings.Contains(text, "$.pointerValue<State>(s).SetErrorf(\"bad %q\", key)") {
2493
+ t.Fatalf("generated override subpackage call was not packed:\n%s", text)
2494
+ }
2495
+ }
2496
+
2497
+ func TestCompilePackagesImportsSelectedExternalFieldTypes(t *testing.T) {
2498
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2499
+ "go.mod": "module example.test/selected-field-import\n\ngo 1.25.3\n",
2500
+ "dep/dep.go": strings.Join([]string{
2501
+ "package dep",
2502
+ "type URL struct { Path string }",
2503
+ "",
2504
+ }, "\n"),
2505
+ "api/api.go": strings.Join([]string{
2506
+ "package api",
2507
+ "import \"example.test/selected-field-import/dep\"",
2508
+ "type Request struct { URL *dep.URL }",
2509
+ "",
2510
+ }, "\n"),
2511
+ "main.go": strings.Join([]string{
2512
+ "package main",
2513
+ "import \"example.test/selected-field-import/api\"",
2514
+ "func requestPath(r *api.Request) string {",
2515
+ " return r.URL.Path",
2516
+ "}",
2517
+ "",
2518
+ }, "\n"),
2519
+ })
2520
+ outputDir := filepath.Join(t.TempDir(), "output")
2521
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir, AllDependencies: true}, nil, nil)
2522
+ if err != nil {
2523
+ t.Fatal(err.Error())
2524
+ }
2525
+
2526
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2527
+ t.Fatal(err.Error())
2528
+ }
2529
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "selected-field-import", "main.gs.ts")
2530
+ content, err := os.ReadFile(outputFile)
2531
+ if err != nil {
2532
+ t.Fatal(err.Error())
2533
+ }
2534
+ text := string(content)
2535
+ for _, want := range []string{
2536
+ "import * as dep from \"@goscript/example.test/selected-field-import/dep/index.js\"",
2537
+ "$.pointerValue<dep.URL>($.pointerValue<api.Request>(r).URL).Path",
2538
+ } {
2539
+ if !strings.Contains(text, want) {
2540
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2541
+ }
2542
+ }
2543
+ }
2544
+
2545
+ func TestCompilePackagesErasesUnavailableOverrideFieldTypes(t *testing.T) {
1103
2546
  moduleDir := writePackageGraphFixture(t, map[string]string{
1104
- "go.mod": "module example.test/variadic-subpackage\n\ngo 1.25.3\n",
1105
- "json/json.go": strings.Join([]string{
1106
- "package json",
1107
- "import \"fmt\"",
1108
- "type State struct { err error }",
1109
- "func (s *State) SetErrorf(format string, a ...any) {",
1110
- " s.err = fmt.Errorf(format, a...)",
1111
- "}",
1112
- "func (s *State) Read(key string) {",
1113
- " s.SetErrorf(\"bad %q\", key)",
2547
+ "go.mod": "module example.test/override-field-type\n\ngo 1.25.3\n",
2548
+ "dep/dep.go": strings.Join([]string{
2549
+ "package dep",
2550
+ "type URL struct { Path string }",
2551
+ "",
2552
+ }, "\n"),
2553
+ "api/api.go": strings.Join([]string{
2554
+ "package api",
2555
+ "import \"example.test/override-field-type/dep\"",
2556
+ "type Request struct { URL *dep.URL }",
2557
+ "",
2558
+ }, "\n"),
2559
+ "main.go": strings.Join([]string{
2560
+ "package main",
2561
+ "import \"example.test/override-field-type/api\"",
2562
+ "func requestPath(r *api.Request) string {",
2563
+ " return r.URL.Path",
1114
2564
  "}",
1115
2565
  "",
1116
2566
  }, "\n"),
1117
2567
  })
2568
+ overrideDir := filepath.Join(t.TempDir(), "gs")
2569
+ writeFixtureFile(t, overrideDir, "example.test/override-field-type/api/index.ts", strings.Join([]string{
2570
+ "export class Request {",
2571
+ " public URL: any = null",
2572
+ "}",
2573
+ "",
2574
+ }, "\n"))
1118
2575
  outputDir := filepath.Join(t.TempDir(), "output")
1119
- comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2576
+ comp, err := NewCompiler(&Config{
2577
+ Dir: moduleDir,
2578
+ OutputPath: outputDir,
2579
+ AllDependencies: true,
2580
+ OverrideDirs: []string{overrideDir},
2581
+ }, nil, nil)
1120
2582
  if err != nil {
1121
2583
  t.Fatal(err.Error())
1122
2584
  }
1123
2585
 
1124
- if _, err := comp.CompilePackages(context.Background(), "./json"); err != nil {
2586
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1125
2587
  t.Fatal(err.Error())
1126
2588
  }
1127
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "variadic-subpackage", "json", "json.gs.ts")
2589
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "override-field-type", "main.gs.ts")
1128
2590
  content, err := os.ReadFile(outputFile)
1129
2591
  if err != nil {
1130
2592
  t.Fatal(err.Error())
1131
2593
  }
1132
2594
  text := string(content)
1133
- want := "$.pointerValue<State>(s).SetErrorf(\"bad %q\", $.arrayToSlice<any>([key]))"
1134
- if !strings.Contains(text, want) {
1135
- t.Fatalf("missing %q in generated output:\n%s", want, text)
2595
+ if !strings.Contains(text, "$.pointerValue<any>($.pointerValue<api.Request>(r).URL).Path") {
2596
+ t.Fatalf("missing erased override field type in generated output:\n%s", text)
1136
2597
  }
1137
- if strings.Contains(text, "$.pointerValue<State>(s).SetErrorf(\"bad %q\", key)") {
1138
- t.Fatalf("generated override subpackage call was not packed:\n%s", text)
2598
+ if strings.Contains(text, "dep.URL") || strings.Contains(text, "pointerValue<URL>") {
2599
+ t.Fatalf("generated output referenced unavailable dependency type:\n%s", text)
1139
2600
  }
1140
2601
  }
1141
2602
 
@@ -1216,6 +2677,49 @@ func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
1216
2677
  }
1217
2678
  }
1218
2679
 
2680
+ func TestCompilePackagesPreservesNamedFunctionResultTypes(t *testing.T) {
2681
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2682
+ "go.mod": "module example.test/namedfuncresult\n\ngo 1.25.3\n",
2683
+ "main.go": strings.Join([]string{
2684
+ "package main",
2685
+ "type Seq func(func(int) bool)",
2686
+ "func values() Seq {",
2687
+ " return nil",
2688
+ "}",
2689
+ "func main() {",
2690
+ " if values() == nil {",
2691
+ " println(\"empty\")",
2692
+ " }",
2693
+ "}",
2694
+ "",
2695
+ }, "\n"),
2696
+ })
2697
+ outputDir := filepath.Join(t.TempDir(), "output")
2698
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2699
+ if err != nil {
2700
+ t.Fatal(err.Error())
2701
+ }
2702
+
2703
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2704
+ t.Fatal(err.Error())
2705
+ }
2706
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "namedfuncresult", "main.gs.ts")
2707
+ content, err := os.ReadFile(outputFile)
2708
+ if err != nil {
2709
+ t.Fatal(err.Error())
2710
+ }
2711
+ text := string(content)
2712
+ for _, want := range []string{
2713
+ "export type Seq = ((_p0: ((_p0: number) => boolean | globalThis.Promise<boolean>) | null) => void) | null",
2714
+ "export function values(): Seq | null",
2715
+ "return (null as Seq | null)",
2716
+ } {
2717
+ if !strings.Contains(text, want) {
2718
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2719
+ }
2720
+ }
2721
+ }
2722
+
1219
2723
  func TestCompilePackagesLowersFunctionIteratorControlFlow(t *testing.T) {
1220
2724
  moduleDir := writePackageGraphFixture(t, map[string]string{
1221
2725
  "go.mod": "module example.test/iterator-control\n\ngo 1.25.3\n",
@@ -1307,35 +2811,289 @@ func TestCompilePackagesLowersFunctionIteratorControlFlow(t *testing.T) {
1307
2811
  t.Fatalf("missing %q in generated output:\n%s", want, text)
1308
2812
  }
1309
2813
  }
1310
- nestedReturn := regexp.MustCompile(`if \(__goscriptRangeReturn\d+\) \{\n\t+__goscriptRangeReturn\d+ = true\n\t+__goscriptRangeReturnValue\d+ = __goscriptRangeReturnValue\d+!\n\t+return false\n\t+\}`)
1311
- if !nestedReturn.MatchString(text) {
1312
- t.Fatalf("missing nested range return propagation:\n%s", text)
2814
+ nestedReturn := regexp.MustCompile(`if \(__goscriptRangeReturn\d+\) \{\n\t+__goscriptRangeReturn\d+ = true\n\t+__goscriptRangeReturnValue\d+ = __goscriptRangeReturnValue\d+!\n\t+return false\n\t+\}`)
2815
+ if !nestedReturn.MatchString(text) {
2816
+ t.Fatalf("missing nested range return propagation:\n%s", text)
2817
+ }
2818
+ }
2819
+
2820
+ func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
2821
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2822
+ "go.mod": "module example.test/async\n\ngo 1.25.3\n",
2823
+ "main.go": strings.Join([]string{
2824
+ "package main",
2825
+ "type Processor interface { Process(v int) int }",
2826
+ "type Worker struct { ch chan int }",
2827
+ "func (w *Worker) Process(v int) int {",
2828
+ " w.ch <- v",
2829
+ " return <-w.ch",
2830
+ "}",
2831
+ "func call(p Processor) int { return p.Process(2) }",
2832
+ "func stopLoop(stop chan struct{}, done chan struct{}) {",
2833
+ " for {",
2834
+ " select {",
2835
+ " case <-stop:",
2836
+ " done <- struct{}{}",
2837
+ " return",
2838
+ " }",
2839
+ " }",
2840
+ "}",
2841
+ "func main() {",
2842
+ " ch := make(chan int, 1)",
2843
+ " defer func() { <-ch }()",
2844
+ " go func() { ch <- 1 }()",
2845
+ " select {",
2846
+ " case v := <-ch:",
2847
+ " println(v)",
2848
+ " default:",
2849
+ " println(\"default\")",
2850
+ " }",
2851
+ " _ = call(&Worker{ch: make(chan int, 1)})",
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
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "async", "main.gs.ts")
2866
+ content, err := os.ReadFile(outputFile)
2867
+ if err != nil {
2868
+ t.Fatal(err.Error())
2869
+ }
2870
+ text := string(content)
2871
+ for _, want := range []string{
2872
+ "Process(v: number): number | globalThis.Promise<number>",
2873
+ "public async Process(v: number): globalThis.Promise<number>",
2874
+ "let ch = $.makeChannel<number>(1, 0, \"both\")",
2875
+ "await $.chanSend($.pointerValue<Worker>(w).ch, v)",
2876
+ "return await $.chanRecv($.pointerValue<Worker>(w).ch)",
2877
+ "await using __defer = new $.AsyncDisposableStack()",
2878
+ "queueMicrotask(async () => { await ($.functionValue(async (): globalThis.Promise<void> => {",
2879
+ "$.selectStatement<any, void>([",
2880
+ "let v = __goscriptSelect1Result.value",
2881
+ "return $.selectVoidReturn()",
2882
+ "await call($.interfaceValue<Processor | null>(new Worker({ch: $.makeChannel<number>(1, 0, \"both\")}), \"*main.Worker\"))",
2883
+ } {
2884
+ if !strings.Contains(text, want) {
2885
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2886
+ }
2887
+ }
2888
+ }
2889
+
2890
+ func TestCompilePackagesMarksSelectReturningIfElseCasesUnreachable(t *testing.T) {
2891
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2892
+ "go.mod": "module example.test/select-if-else\n\ngo 1.25.3\n",
2893
+ "main.go": strings.Join([]string{
2894
+ "package main",
2895
+ "import \"context\"",
2896
+ "func finish(ctx context.Context, ch <-chan int, client bool) (int, error) {",
2897
+ " select {",
2898
+ " case <-ch:",
2899
+ " if client {",
2900
+ " return 1, nil",
2901
+ " } else {",
2902
+ " return 2, nil",
2903
+ " }",
2904
+ " case <-ctx.Done():",
2905
+ " return 3, ctx.Err()",
2906
+ " }",
2907
+ "}",
2908
+ "",
2909
+ }, "\n"),
2910
+ })
2911
+ outputDir := filepath.Join(t.TempDir(), "output")
2912
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2913
+ if err != nil {
2914
+ t.Fatal(err.Error())
2915
+ }
2916
+
2917
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2918
+ t.Fatal(err.Error())
2919
+ }
2920
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "select-if-else", "main.gs.ts")
2921
+ content, err := os.ReadFile(outputFile)
2922
+ if err != nil {
2923
+ t.Fatal(err.Error())
2924
+ }
2925
+ text := string(content)
2926
+ for _, want := range []string{
2927
+ "export async function finish(ctx: context.Context | null, ch: $.Channel<number> | null, client: boolean): globalThis.Promise<[number, $.GoError]>",
2928
+ "if (__goscriptSelect0HasReturn) {",
2929
+ "throw new Error(\"unreachable select\")",
2930
+ } {
2931
+ if !strings.Contains(text, want) {
2932
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2933
+ }
2934
+ }
2935
+ }
2936
+
2937
+ func TestCompilePackagesAddsUnreachableReturnFallback(t *testing.T) {
2938
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2939
+ "go.mod": "module example.test/select-named\n\ngo 1.25.3\n",
2940
+ "main.go": strings.Join([]string{
2941
+ "package main",
2942
+ "import \"context\"",
2943
+ "func wait(ctx context.Context, ch <-chan error) (rerr error) {",
2944
+ " select {",
2945
+ " case <-ctx.Done():",
2946
+ " return context.Canceled",
2947
+ " case err := <-ch:",
2948
+ " return err",
2949
+ " }",
2950
+ "}",
2951
+ "",
2952
+ }, "\n"),
2953
+ })
2954
+ outputDir := filepath.Join(t.TempDir(), "output")
2955
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
2956
+ if err != nil {
2957
+ t.Fatal(err.Error())
2958
+ }
2959
+
2960
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
2961
+ t.Fatal(err.Error())
2962
+ }
2963
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "select-named", "main.gs.ts")
2964
+ content, err := os.ReadFile(outputFile)
2965
+ if err != nil {
2966
+ t.Fatal(err.Error())
2967
+ }
2968
+ text := string(content)
2969
+ for _, want := range []string{
2970
+ "export async function wait(ctx: context.Context | null, ch: $.Channel<$.GoError> | null): globalThis.Promise<$.GoError>",
2971
+ "let rerr: $.GoError = null as $.GoError",
2972
+ "throw new globalThis.Error(\"goscript: unreachable return\")",
2973
+ } {
2974
+ if !strings.Contains(text, want) {
2975
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
2976
+ }
2977
+ }
2978
+ }
2979
+
2980
+ func TestCompilePackagesAnnotatesShortDeclInterfaceValues(t *testing.T) {
2981
+ moduleDir := writePackageGraphFixture(t, map[string]string{
2982
+ "go.mod": "module example.test/interface-short-decl\n\ngo 1.25.3\n",
2983
+ "main.go": strings.Join([]string{
2984
+ "package main",
2985
+ "type Reader interface { Read() int }",
2986
+ "type impl struct{ value int }",
2987
+ "func (i *impl) Read() int { return i.value }",
2988
+ "func replacement() Reader { return &impl{value: 2} }",
2989
+ "func use(r Reader, swap bool) int {",
2990
+ " if r == nil {",
2991
+ " return 0",
2992
+ " }",
2993
+ " current := r",
2994
+ " if swap {",
2995
+ " current = replacement()",
2996
+ " }",
2997
+ " return current.Read()",
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", "interface-short-decl", "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, "let current: Reader | null = r") {
3018
+ t.Fatalf("missing interface short declaration annotation:\n%s", text)
3019
+ }
3020
+ }
3021
+
3022
+ func TestCompilePackagesPropagatesImmediateFuncLitAsync(t *testing.T) {
3023
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3024
+ "go.mod": "module example.test/immediate-func-lit-async\n\ngo 1.25.3\n",
3025
+ "main.go": strings.Join([]string{
3026
+ "package main",
3027
+ "import \"sync\"",
3028
+ "type resolver struct {",
3029
+ " parent *resolver",
3030
+ " mutex sync.Mutex",
3031
+ "}",
3032
+ "func (r *resolver) lookup() (int, error) {",
3033
+ " value := func() int {",
3034
+ " r.mutex.Lock()",
3035
+ " defer r.mutex.Unlock()",
3036
+ " return 7",
3037
+ " }()",
3038
+ " if r.parent != nil {",
3039
+ " return r.parent.lookup()",
3040
+ " }",
3041
+ " return value, nil",
3042
+ "}",
3043
+ "func use(r *resolver) (int, error) {",
3044
+ " return r.lookup()",
3045
+ "}",
3046
+ "func main() {}",
3047
+ "",
3048
+ }, "\n"),
3049
+ })
3050
+ outputDir := filepath.Join(t.TempDir(), "output")
3051
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3052
+ if err != nil {
3053
+ t.Fatal(err.Error())
3054
+ }
3055
+
3056
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3057
+ t.Fatal(err.Error())
3058
+ }
3059
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "immediate-func-lit-async", "main.gs.ts")
3060
+ content, err := os.ReadFile(outputFile)
3061
+ if err != nil {
3062
+ t.Fatal(err.Error())
3063
+ }
3064
+ text := string(content)
3065
+ for _, want := range []string{
3066
+ "public async lookup(): globalThis.Promise<[number, $.GoError]>",
3067
+ "return await resolver.prototype.lookup.call($.pointerValue<resolver>(r).parent)",
3068
+ "export async function use(r: resolver | $.VarRef<resolver> | null): globalThis.Promise<[number, $.GoError]>",
3069
+ "return await resolver.prototype.lookup.call(r)",
3070
+ } {
3071
+ if !strings.Contains(text, want) {
3072
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
3073
+ }
3074
+ }
3075
+ if strings.Contains(text, "const __goscriptReturn0 = resolver.prototype.lookup.call") {
3076
+ t.Fatalf("immediate func-literal async method call was not awaited:\n%s", text)
1313
3077
  }
1314
3078
  }
1315
3079
 
1316
- func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
3080
+ func TestCompilePackagesParenthesizesAsyncFieldReceivers(t *testing.T) {
1317
3081
  moduleDir := writePackageGraphFixture(t, map[string]string{
1318
- "go.mod": "module example.test/async\n\ngo 1.25.3\n",
3082
+ "go.mod": "module example.test/asyncfield\n\ngo 1.25.3\n",
1319
3083
  "main.go": strings.Join([]string{
1320
3084
  "package main",
1321
- "type Processor interface { Process(v int) int }",
1322
- "type Worker struct { ch chan int }",
1323
- "func (w *Worker) Process(v int) int {",
1324
- " w.ch <- v",
1325
- " return <-w.ch",
3085
+ "type Result struct { ok bool }",
3086
+ "type Box struct { ch chan int }",
3087
+ "func (b *Box) next() Result {",
3088
+ " b.ch <- 1",
3089
+ " return Result{ok: true}",
3090
+ "}",
3091
+ "func (b *Box) OK() bool {",
3092
+ " return b.next().ok",
1326
3093
  "}",
1327
- "func call(p Processor) int { return p.Process(2) }",
1328
3094
  "func main() {",
1329
- " ch := make(chan int, 1)",
1330
- " defer func() { <-ch }()",
1331
- " go func() { ch <- 1 }()",
1332
- " select {",
1333
- " case v := <-ch:",
1334
- " println(v)",
1335
- " default:",
1336
- " println(\"default\")",
1337
- " }",
1338
- " _ = call(&Worker{ch: make(chan int, 1)})",
3095
+ " box := &Box{ch: make(chan int, 1)}",
3096
+ " println(box.OK())",
1339
3097
  "}",
1340
3098
  "",
1341
3099
  }, "\n"),
@@ -1349,27 +3107,17 @@ func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
1349
3107
  if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1350
3108
  t.Fatal(err.Error())
1351
3109
  }
1352
- outputFile := filepath.Join(outputDir, "@goscript", "example.test", "async", "main.gs.ts")
3110
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "asyncfield", "main.gs.ts")
1353
3111
  content, err := os.ReadFile(outputFile)
1354
3112
  if err != nil {
1355
3113
  t.Fatal(err.Error())
1356
3114
  }
1357
3115
  text := string(content)
1358
- for _, want := range []string{
1359
- "Process(v: number): globalThis.Promise<number>",
1360
- "public async Process(v: number): globalThis.Promise<number>",
1361
- "let ch = $.makeChannel<number>(1, 0, \"both\")",
1362
- "await $.chanSend($.pointerValue<Worker>(w).ch, v)",
1363
- "return await $.chanRecv($.pointerValue<Worker>(w).ch)",
1364
- "await using __defer = new $.AsyncDisposableStack()",
1365
- "queueMicrotask(async () => { await ($.functionValue(async (): globalThis.Promise<void> => {",
1366
- "$.selectStatement<any, void>([",
1367
- "let v = result.value",
1368
- "await call($.interfaceValue<Processor | null>(new Worker({ch: $.makeChannel<number>(1, 0, \"both\")}), \"*main.Worker\"))",
1369
- } {
1370
- if !strings.Contains(text, want) {
1371
- t.Fatalf("missing %q in generated output:\n%s", want, text)
1372
- }
3116
+ if !strings.Contains(text, "return (await Box.prototype.next.call(b)).ok") {
3117
+ t.Fatalf("async field receiver was not parenthesized:\n%s", text)
3118
+ }
3119
+ if strings.Contains(text, "return await Box.prototype.next.call(b).ok") {
3120
+ t.Fatalf("async field receiver selected the promise before await:\n%s", text)
1373
3121
  }
1374
3122
  }
1375
3123
 
@@ -1650,6 +3398,333 @@ func TestCompilePackagesLowersUnaryBitwiseComplement(t *testing.T) {
1650
3398
  }
1651
3399
  }
1652
3400
 
3401
+ func TestCompilePackagesParenthesizesRepeatedUnarySigns(t *testing.T) {
3402
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3403
+ "go.mod": "module example.test/unary-signs\n\ngo 1.25.3\n",
3404
+ "constants.go": strings.Join([]string{
3405
+ "package main",
3406
+ "const extOffset = -0x1000",
3407
+ "",
3408
+ }, "\n"),
3409
+ "main.go": strings.Join([]string{
3410
+ "package main",
3411
+ "type Extension int32",
3412
+ "type LoadExtension struct { Num Extension }",
3413
+ "func Decode(k int32) LoadExtension {",
3414
+ " return LoadExtension{Num: Extension(-extOffset + k)}",
3415
+ "}",
3416
+ "",
3417
+ }, "\n"),
3418
+ })
3419
+ outputDir := filepath.Join(t.TempDir(), "output")
3420
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3421
+ if err != nil {
3422
+ t.Fatal(err.Error())
3423
+ }
3424
+
3425
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3426
+ t.Fatal(err.Error())
3427
+ }
3428
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "unary-signs", "main.gs.ts")
3429
+ content, err := os.ReadFile(outputFile)
3430
+ if err != nil {
3431
+ t.Fatal(err.Error())
3432
+ }
3433
+ text := string(content)
3434
+ if strings.Contains(text, "--4096") {
3435
+ t.Fatalf("generated invalid decrement token:\n%s", text)
3436
+ }
3437
+ if !strings.Contains(text, "-(-4096) + k") {
3438
+ t.Fatalf("missing parenthesized negative constant:\n%s", text)
3439
+ }
3440
+ }
3441
+
3442
+ func TestCompilePackagesNormalizesWideIntegerReturnTargets(t *testing.T) {
3443
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3444
+ "go.mod": "module example.test/wide-return\n\ngo 1.25.3\n",
3445
+ "main.go": strings.Join([]string{
3446
+ "package main",
3447
+ "import \"hash\"",
3448
+ "func Read(h hash.Hash64) uint64 {",
3449
+ " return h.Sum64()",
3450
+ "}",
3451
+ "",
3452
+ }, "\n"),
3453
+ })
3454
+ outputDir := filepath.Join(t.TempDir(), "output")
3455
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3456
+ if err != nil {
3457
+ t.Fatal(err.Error())
3458
+ }
3459
+
3460
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3461
+ t.Fatal(err.Error())
3462
+ }
3463
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "wide-return", "main.gs.ts")
3464
+ content, err := os.ReadFile(outputFile)
3465
+ if err != nil {
3466
+ t.Fatal(err.Error())
3467
+ }
3468
+ text := string(content)
3469
+ if !strings.Contains(text, "return $.uint($.pointerValue<Exclude<hash.Hash64, null>>(h).Sum64(), 64)") {
3470
+ t.Fatalf("missing uint64 return normalization:\n%s", text)
3471
+ }
3472
+ }
3473
+
3474
+ func TestCompilePackagesUnwrapsImportedVarRefValueMethodReceiver(t *testing.T) {
3475
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3476
+ "go.mod": "module example.test/imported-varref-receiver\n\ngo 1.25.3\n",
3477
+ "dep/dep.go": strings.Join([]string{
3478
+ "package dep",
3479
+ "type Info struct { Count int }",
3480
+ "func (i Info) Enabled() bool { return i.Count > 0 }",
3481
+ "func addInfo(i *Info) { i.Count = 1 }",
3482
+ "var CPU Info",
3483
+ "func init() { addInfo(&CPU) }",
3484
+ "",
3485
+ }, "\n"),
3486
+ "main.go": strings.Join([]string{
3487
+ "package main",
3488
+ "import \"example.test/imported-varref-receiver/dep\"",
3489
+ "func Enabled() bool {",
3490
+ " return dep.CPU.Enabled()",
3491
+ "}",
3492
+ "",
3493
+ }, "\n"),
3494
+ })
3495
+ outputDir := filepath.Join(t.TempDir(), "output")
3496
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3497
+ if err != nil {
3498
+ t.Fatal(err.Error())
3499
+ }
3500
+
3501
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3502
+ t.Fatal(err.Error())
3503
+ }
3504
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "imported-varref-receiver", "main.gs.ts")
3505
+ content, err := os.ReadFile(outputFile)
3506
+ if err != nil {
3507
+ t.Fatal(err.Error())
3508
+ }
3509
+ text := string(content)
3510
+ if !strings.Contains(text, "$.cloneStructValue($.pointerValue<dep.Info>(dep.CPU))") {
3511
+ t.Fatalf("missing imported VarRef receiver unwrap:\n%s", text)
3512
+ }
3513
+ if strings.Contains(text, "$.cloneStructValue(dep.CPU))") {
3514
+ t.Fatalf("imported VarRef receiver stayed wrapped:\n%s", text)
3515
+ }
3516
+ }
3517
+
3518
+ func TestCompilePackagesUnwrapsOverridePointerMethodReceiver(t *testing.T) {
3519
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3520
+ "go.mod": "module example.test/override-pointer-receiver\n\ngo 1.25.3\n",
3521
+ "main.go": strings.Join([]string{
3522
+ "package main",
3523
+ "import \"sync/atomic\"",
3524
+ "func Read(active *atomic.Int32) int32 {",
3525
+ " return active.Load()",
3526
+ "}",
3527
+ "",
3528
+ }, "\n"),
3529
+ })
3530
+ outputDir := filepath.Join(t.TempDir(), "output")
3531
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3532
+ if err != nil {
3533
+ t.Fatal(err.Error())
3534
+ }
3535
+
3536
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3537
+ t.Fatal(err.Error())
3538
+ }
3539
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "override-pointer-receiver", "main.gs.ts")
3540
+ content, err := os.ReadFile(outputFile)
3541
+ if err != nil {
3542
+ t.Fatal(err.Error())
3543
+ }
3544
+ text := string(content)
3545
+ if !strings.Contains(text, "atomic.Int32.prototype.Load.call($.pointerValue<atomic.Int32>(active))") {
3546
+ t.Fatalf("override pointer receiver was not unwrapped:\n%s", text)
3547
+ }
3548
+ if strings.Contains(text, "atomic.Int32.prototype.Load.call(active)") {
3549
+ t.Fatalf("override pointer receiver stayed wrapped:\n%s", text)
3550
+ }
3551
+ }
3552
+
3553
+ func TestCompilePackagesUnwrapsImportedArrayPackageVarReads(t *testing.T) {
3554
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3555
+ "go.mod": "module example.test/imported-array-var\n\ngo 1.25.3\n",
3556
+ "dep/dep.go": strings.Join([]string{
3557
+ "package dep",
3558
+ "var Table = [2]int{3, 5}",
3559
+ "func touch(v *[2]int) { v[0]++ }",
3560
+ "func init() { touch(&Table) }",
3561
+ "func Sum(v [2]int) int { return v[0] + v[1] }",
3562
+ "",
3563
+ }, "\n"),
3564
+ "main.go": strings.Join([]string{
3565
+ "package main",
3566
+ "import \"example.test/imported-array-var/dep\"",
3567
+ "func Read() int {",
3568
+ " return dep.Table[1] + dep.Sum(dep.Table)",
3569
+ "}",
3570
+ "",
3571
+ }, "\n"),
3572
+ })
3573
+ outputDir := filepath.Join(t.TempDir(), "output")
3574
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3575
+ if err != nil {
3576
+ t.Fatal(err.Error())
3577
+ }
3578
+
3579
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3580
+ t.Fatal(err.Error())
3581
+ }
3582
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "imported-array-var", "main.gs.ts")
3583
+ content, err := os.ReadFile(outputFile)
3584
+ if err != nil {
3585
+ t.Fatal(err.Error())
3586
+ }
3587
+ text := string(content)
3588
+ for _, want := range []string{
3589
+ "$.pointerValue<number[]>(dep.Table)[1]",
3590
+ "dep.Sum($.pointerValue<number[]>(dep.Table))",
3591
+ } {
3592
+ if !strings.Contains(text, want) {
3593
+ t.Fatalf("missing imported array package var read %q:\n%s", want, text)
3594
+ }
3595
+ }
3596
+ }
3597
+
3598
+ func TestCompilePackagesAddressesImportedArrayPackageVarsAsRefs(t *testing.T) {
3599
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3600
+ "go.mod": "module example.test/imported-array-var-address\n\ngo 1.25.3\n",
3601
+ "dep/dep.go": strings.Join([]string{
3602
+ "package dep",
3603
+ "var Table = [2]int{3, 5}",
3604
+ "func touch(v *[2]int) { v[0]++ }",
3605
+ "func init() { touch(&Table) }",
3606
+ "func SumPtr(v *[2]int) int { return v[0] + v[1] }",
3607
+ "",
3608
+ }, "\n"),
3609
+ "main.go": strings.Join([]string{
3610
+ "package main",
3611
+ "import \"example.test/imported-array-var-address/dep\"",
3612
+ "func Read() int {",
3613
+ " return dep.SumPtr(&dep.Table)",
3614
+ "}",
3615
+ "",
3616
+ }, "\n"),
3617
+ })
3618
+ outputDir := filepath.Join(t.TempDir(), "output")
3619
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3620
+ if err != nil {
3621
+ t.Fatal(err.Error())
3622
+ }
3623
+
3624
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3625
+ t.Fatal(err.Error())
3626
+ }
3627
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "imported-array-var-address", "main.gs.ts")
3628
+ content, err := os.ReadFile(outputFile)
3629
+ if err != nil {
3630
+ t.Fatal(err.Error())
3631
+ }
3632
+ text := string(content)
3633
+ if !strings.Contains(text, "dep.SumPtr(dep.Table)") {
3634
+ t.Fatalf("missing imported array package var address:\n%s", text)
3635
+ }
3636
+ if strings.Contains(text, "dep.SumPtr($.pointerValue<number[]>(dep.Table))") ||
3637
+ strings.Contains(text, "dep._fields.Table") {
3638
+ t.Fatalf("imported array package var address was lowered as a read or field:\n%s", text)
3639
+ }
3640
+ }
3641
+
3642
+ func TestCompilePackagesUnwrapsAliasedArrayPackageVarReads(t *testing.T) {
3643
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3644
+ "go.mod": "module example.test/aliased-array-var\n\ngo 1.25.3\n",
3645
+ "table.go": strings.Join([]string{
3646
+ "package main",
3647
+ "var Table = [2]int{3, 5}",
3648
+ "func touch(v *[2]int) { v[0]++ }",
3649
+ "func init() { touch(&Table) }",
3650
+ "func Sum(v [2]int) int { return v[0] + v[1] }",
3651
+ "",
3652
+ }, "\n"),
3653
+ "read.go": strings.Join([]string{
3654
+ "package main",
3655
+ "func Read() int {",
3656
+ " return Table[1] + Sum(Table)",
3657
+ "}",
3658
+ "",
3659
+ }, "\n"),
3660
+ })
3661
+ outputDir := filepath.Join(t.TempDir(), "output")
3662
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3663
+ if err != nil {
3664
+ t.Fatal(err.Error())
3665
+ }
3666
+
3667
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3668
+ t.Fatal(err.Error())
3669
+ }
3670
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "aliased-array-var", "read.gs.ts")
3671
+ content, err := os.ReadFile(outputFile)
3672
+ if err != nil {
3673
+ t.Fatal(err.Error())
3674
+ }
3675
+ text := string(content)
3676
+ for _, want := range []string{
3677
+ "$.pointerValue<number[]>(__goscript_table.Table)[1]",
3678
+ "Sum($.pointerValue<number[]>(__goscript_table.Table))",
3679
+ } {
3680
+ if !strings.Contains(text, want) {
3681
+ t.Fatalf("missing aliased array package var read %q:\n%s", want, text)
3682
+ }
3683
+ }
3684
+ }
3685
+
3686
+ func TestCompilePackagesAddressesAliasedArrayPackageVarsAsRefs(t *testing.T) {
3687
+ moduleDir := writePackageGraphFixture(t, map[string]string{
3688
+ "go.mod": "module example.test/aliased-array-var-address\n\ngo 1.25.3\n",
3689
+ "table.go": strings.Join([]string{
3690
+ "package main",
3691
+ "var Table = [2]int{3, 5}",
3692
+ "func touch(v *[2]int) { v[0]++ }",
3693
+ "func init() { touch(&Table) }",
3694
+ "func SumPtr(v *[2]int) int { return v[0] + v[1] }",
3695
+ "",
3696
+ }, "\n"),
3697
+ "read.go": strings.Join([]string{
3698
+ "package main",
3699
+ "func Read() int {",
3700
+ " return SumPtr(&Table)",
3701
+ "}",
3702
+ "",
3703
+ }, "\n"),
3704
+ })
3705
+ outputDir := filepath.Join(t.TempDir(), "output")
3706
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
3707
+ if err != nil {
3708
+ t.Fatal(err.Error())
3709
+ }
3710
+
3711
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
3712
+ t.Fatal(err.Error())
3713
+ }
3714
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "aliased-array-var-address", "read.gs.ts")
3715
+ content, err := os.ReadFile(outputFile)
3716
+ if err != nil {
3717
+ t.Fatal(err.Error())
3718
+ }
3719
+ text := string(content)
3720
+ if !strings.Contains(text, "SumPtr(__goscript_table.Table)") {
3721
+ t.Fatalf("missing aliased array package var address:\n%s", text)
3722
+ }
3723
+ if strings.Contains(text, "SumPtr($.pointerValue<number[]>(__goscript_table.Table))") {
3724
+ t.Fatalf("aliased array package var address was lowered as a read:\n%s", text)
3725
+ }
3726
+ }
3727
+
1653
3728
  func TestCompileSourceToTypeScriptCompilesSingleFile(t *testing.T) {
1654
3729
  output, err := CompileSourceToTypeScript("package main\nfunc main() { println(\"hi\") }\n", "main")
1655
3730
  if err != nil {