goscript 0.1.0 → 0.1.1

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 (327) hide show
  1. package/README.md +267 -255
  2. package/cmd/goscript/cmd-test.go +193 -0
  3. package/cmd/goscript/cmd-test_test.go +76 -0
  4. package/cmd/goscript/main.go +1 -0
  5. package/compiler/build-flags.go +38 -0
  6. package/compiler/compile-request.go +2 -0
  7. package/compiler/compliance_test.go +0 -8
  8. package/compiler/gotest/owner.go +24 -0
  9. package/compiler/gotest/package-result.go +67 -0
  10. package/compiler/gotest/request.go +145 -0
  11. package/compiler/gotest/result.go +28 -0
  12. package/compiler/gotest/runner.go +588 -0
  13. package/compiler/gotest/runner_test.go +627 -0
  14. package/compiler/gotest/test.go +9 -0
  15. package/compiler/index.test.ts +1 -1
  16. package/compiler/lowered-program.go +71 -19
  17. package/compiler/lowering.go +5065 -569
  18. package/compiler/override-facts.go +307 -0
  19. package/compiler/override-registry.go +50 -189
  20. package/compiler/override-registry_test.go +47 -0
  21. package/compiler/package-graph.go +50 -27
  22. package/compiler/package-graph_test.go +37 -2
  23. package/compiler/package-test-function.go +9 -0
  24. package/compiler/package-test-graph-package.go +40 -0
  25. package/compiler/package-test-graph-variant.go +105 -0
  26. package/compiler/package-test-graph.go +117 -0
  27. package/compiler/package-test-graph_test.go +144 -0
  28. package/compiler/runtime-contract.go +189 -29
  29. package/compiler/runtime-contract_test.go +44 -30
  30. package/compiler/semantic-model-types.go +9 -6
  31. package/compiler/semantic-model.go +538 -38
  32. package/compiler/semantic-model_test.go +55 -0
  33. package/compiler/service.go +1 -1
  34. package/compiler/skeleton_test.go +679 -49
  35. package/compiler/tsworkspace/owner.go +334 -0
  36. package/compiler/tsworkspace/owner_test.go +93 -0
  37. package/compiler/tsworkspace/result.go +17 -0
  38. package/compiler/typescript-emitter.go +459 -82
  39. package/compiler/wasm/compile.go +1 -1
  40. package/compiler/wasm/compile_test.go +61 -11
  41. package/compiler/wasm_api.go +172 -7
  42. package/dist/gs/builtin/builtin.d.ts +20 -2
  43. package/dist/gs/builtin/builtin.js +194 -6
  44. package/dist/gs/builtin/builtin.js.map +1 -1
  45. package/dist/gs/builtin/channel.d.ts +8 -0
  46. package/dist/gs/builtin/channel.js +12 -0
  47. package/dist/gs/builtin/channel.js.map +1 -1
  48. package/dist/gs/builtin/slice.d.ts +22 -2
  49. package/dist/gs/builtin/slice.js +216 -44
  50. package/dist/gs/builtin/slice.js.map +1 -1
  51. package/dist/gs/builtin/type.d.ts +5 -2
  52. package/dist/gs/builtin/type.js +83 -24
  53. package/dist/gs/builtin/type.js.map +1 -1
  54. package/dist/gs/builtin/varRef.d.ts +5 -0
  55. package/dist/gs/builtin/varRef.js +23 -0
  56. package/dist/gs/builtin/varRef.js.map +1 -1
  57. package/dist/gs/bytes/buffer.gs.js +48 -44
  58. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  59. package/dist/gs/bytes/reader.gs.js +20 -18
  60. package/dist/gs/bytes/reader.gs.js.map +1 -1
  61. package/dist/gs/context/context.d.ts +5 -4
  62. package/dist/gs/context/context.js +10 -10
  63. package/dist/gs/context/context.js.map +1 -1
  64. package/dist/gs/crypto/internal/fips140deps/byteorder/index.d.ts +1 -0
  65. package/dist/gs/crypto/internal/fips140deps/byteorder/index.js +2 -0
  66. package/dist/gs/crypto/internal/fips140deps/byteorder/index.js.map +1 -0
  67. package/dist/gs/crypto/internal/fips140deps/godebug/index.d.ts +1 -0
  68. package/dist/gs/crypto/internal/fips140deps/godebug/index.js +2 -0
  69. package/dist/gs/crypto/internal/fips140deps/godebug/index.js.map +1 -0
  70. package/dist/gs/embed/index.d.ts +7 -0
  71. package/dist/gs/embed/index.js +16 -0
  72. package/dist/gs/embed/index.js.map +1 -0
  73. package/dist/gs/encoding/json/index.d.ts +1 -0
  74. package/dist/gs/encoding/json/index.js +18 -0
  75. package/dist/gs/encoding/json/index.js.map +1 -1
  76. package/dist/gs/errors/errors.d.ts +4 -0
  77. package/dist/gs/errors/errors.js +81 -0
  78. package/dist/gs/errors/errors.js.map +1 -1
  79. package/dist/gs/fmt/fmt.d.ts +4 -4
  80. package/dist/gs/fmt/fmt.js +42 -11
  81. package/dist/gs/fmt/fmt.js.map +1 -1
  82. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +35 -0
  83. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +211 -1
  84. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  85. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +189 -0
  86. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +825 -0
  87. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -0
  88. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +163 -0
  89. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +449 -0
  90. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -0
  91. package/dist/gs/github.com/klauspost/compress/internal/le/index.d.ts +9 -0
  92. package/dist/gs/github.com/klauspost/compress/internal/le/index.js +71 -0
  93. package/dist/gs/github.com/klauspost/compress/internal/le/index.js.map +1 -0
  94. package/dist/gs/go/internal/scannerhooks/index.d.ts +3 -0
  95. package/dist/gs/go/internal/scannerhooks/index.js +5 -0
  96. package/dist/gs/go/internal/scannerhooks/index.js.map +1 -0
  97. package/dist/gs/go/scanner/index.d.ts +13 -0
  98. package/dist/gs/go/scanner/index.js +35 -0
  99. package/dist/gs/go/scanner/index.js.map +1 -1
  100. package/dist/gs/go/token/index.d.ts +156 -0
  101. package/dist/gs/go/token/index.js +500 -4
  102. package/dist/gs/go/token/index.js.map +1 -1
  103. package/dist/gs/internal/abi/index.d.ts +4 -0
  104. package/dist/gs/internal/abi/index.js +10 -0
  105. package/dist/gs/internal/abi/index.js.map +1 -1
  106. package/dist/gs/internal/bytealg/index.d.ts +2 -0
  107. package/dist/gs/internal/bytealg/index.js +14 -0
  108. package/dist/gs/internal/bytealg/index.js.map +1 -1
  109. package/dist/gs/internal/byteorder/index.d.ts +8 -2
  110. package/dist/gs/internal/byteorder/index.js +56 -25
  111. package/dist/gs/internal/byteorder/index.js.map +1 -1
  112. package/dist/gs/internal/godebug/index.d.ts +12 -0
  113. package/dist/gs/internal/godebug/index.js +30 -0
  114. package/dist/gs/internal/godebug/index.js.map +1 -0
  115. package/dist/gs/io/fs/index.d.ts +1 -0
  116. package/dist/gs/io/fs/index.js +1 -0
  117. package/dist/gs/io/fs/index.js.map +1 -1
  118. package/dist/gs/io/fs/readlink.d.ts +8 -0
  119. package/dist/gs/io/fs/readlink.js +64 -0
  120. package/dist/gs/io/fs/readlink.js.map +1 -0
  121. package/dist/gs/io/fs/walk.d.ts +3 -3
  122. package/dist/gs/io/fs/walk.js +7 -7
  123. package/dist/gs/io/fs/walk.js.map +1 -1
  124. package/dist/gs/io/io.d.ts +40 -6
  125. package/dist/gs/io/io.js +151 -26
  126. package/dist/gs/io/io.js.map +1 -1
  127. package/dist/gs/maps/iter.d.ts +3 -3
  128. package/dist/gs/maps/iter.js +3 -3
  129. package/dist/gs/maps/iter.js.map +1 -1
  130. package/dist/gs/maps/maps.d.ts +2 -2
  131. package/dist/gs/maps/maps.js +1 -1
  132. package/dist/gs/maps/maps.js.map +1 -1
  133. package/dist/gs/math/bits/index.d.ts +13 -4
  134. package/dist/gs/math/bits/index.js +66 -34
  135. package/dist/gs/math/bits/index.js.map +1 -1
  136. package/dist/gs/math/const.gs.d.ts +5 -5
  137. package/dist/gs/math/const.gs.js +4 -4
  138. package/dist/gs/math/const.gs.js.map +1 -1
  139. package/dist/gs/mime/index.d.ts +1 -0
  140. package/dist/gs/mime/index.js +50 -0
  141. package/dist/gs/mime/index.js.map +1 -0
  142. package/dist/gs/net/http/httptest/index.d.ts +11 -0
  143. package/dist/gs/net/http/httptest/index.js +21 -0
  144. package/dist/gs/net/http/httptest/index.js.map +1 -0
  145. package/dist/gs/net/http/index.d.ts +27 -0
  146. package/dist/gs/net/http/index.js +61 -0
  147. package/dist/gs/net/http/index.js.map +1 -0
  148. package/dist/gs/os/dir_unix.gs.js +2 -2
  149. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  150. package/dist/gs/os/types_js.gs.js.map +1 -1
  151. package/dist/gs/path/filepath/match.js +165 -3
  152. package/dist/gs/path/filepath/match.js.map +1 -1
  153. package/dist/gs/path/filepath/path.d.ts +3 -1
  154. package/dist/gs/path/filepath/path.js +133 -4
  155. package/dist/gs/path/filepath/path.js.map +1 -1
  156. package/dist/gs/path/path.d.ts +4 -1
  157. package/dist/gs/path/path.js +16 -4
  158. package/dist/gs/path/path.js.map +1 -1
  159. package/dist/gs/reflect/index.d.ts +1 -1
  160. package/dist/gs/reflect/index.js +1 -1
  161. package/dist/gs/reflect/index.js.map +1 -1
  162. package/dist/gs/reflect/map.js +3 -0
  163. package/dist/gs/reflect/map.js.map +1 -1
  164. package/dist/gs/reflect/type.d.ts +7 -4
  165. package/dist/gs/reflect/type.js +148 -7
  166. package/dist/gs/reflect/type.js.map +1 -1
  167. package/dist/gs/runtime/debug/index.d.ts +2 -0
  168. package/dist/gs/runtime/debug/index.js +8 -0
  169. package/dist/gs/runtime/debug/index.js.map +1 -0
  170. package/dist/gs/runtime/runtime.d.ts +35 -3
  171. package/dist/gs/runtime/runtime.js +72 -0
  172. package/dist/gs/runtime/runtime.js.map +1 -1
  173. package/dist/gs/slices/slices.d.ts +24 -5
  174. package/dist/gs/slices/slices.js +214 -5
  175. package/dist/gs/slices/slices.js.map +1 -1
  176. package/dist/gs/sort/slice.gs.d.ts +3 -3
  177. package/dist/gs/sort/slice.gs.js +6 -6
  178. package/dist/gs/sort/slice.gs.js.map +1 -1
  179. package/dist/gs/sort/sort.gs.d.ts +4 -4
  180. package/dist/gs/sort/sort.gs.js +11 -8
  181. package/dist/gs/sort/sort.gs.js.map +1 -1
  182. package/dist/gs/strings/builder.d.ts +1 -1
  183. package/dist/gs/strings/builder.js +3 -2
  184. package/dist/gs/strings/builder.js.map +1 -1
  185. package/dist/gs/sync/atomic/type.gs.d.ts +9 -8
  186. package/dist/gs/sync/atomic/type.gs.js +0 -2
  187. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  188. package/dist/gs/sync/sync.d.ts +2 -0
  189. package/dist/gs/sync/sync.js +27 -0
  190. package/dist/gs/sync/sync.js.map +1 -1
  191. package/dist/gs/syscall/constants.d.ts +36 -24
  192. package/dist/gs/syscall/constants.js +12 -0
  193. package/dist/gs/syscall/constants.js.map +1 -1
  194. package/dist/gs/syscall/errors.d.ts +2 -0
  195. package/dist/gs/syscall/errors.js +8 -0
  196. package/dist/gs/syscall/errors.js.map +1 -1
  197. package/dist/gs/syscall/fs.d.ts +43 -0
  198. package/dist/gs/syscall/fs.js +102 -0
  199. package/dist/gs/syscall/fs.js.map +1 -1
  200. package/dist/gs/syscall/js/index.d.ts +90 -0
  201. package/dist/gs/syscall/js/index.js +375 -0
  202. package/dist/gs/syscall/js/index.js.map +1 -0
  203. package/dist/gs/syscall/types.d.ts +22 -0
  204. package/dist/gs/syscall/types.js +45 -1
  205. package/dist/gs/syscall/types.js.map +1 -1
  206. package/dist/gs/testing/index.d.ts +1 -0
  207. package/dist/gs/testing/index.js +2 -0
  208. package/dist/gs/testing/index.js.map +1 -0
  209. package/dist/gs/testing/testing.d.ts +77 -0
  210. package/dist/gs/testing/testing.js +301 -0
  211. package/dist/gs/testing/testing.js.map +1 -0
  212. package/dist/gs/time/time.d.ts +41 -4
  213. package/dist/gs/time/time.js +205 -36
  214. package/dist/gs/time/time.js.map +1 -1
  215. package/dist/gs/unicode/unicode.d.ts +23 -1
  216. package/dist/gs/unicode/unicode.js +79 -10
  217. package/dist/gs/unicode/unicode.js.map +1 -1
  218. package/dist/gs/unicode/utf8/utf8.d.ts +4 -4
  219. package/dist/gs/unicode/utf8/utf8.js +24 -11
  220. package/dist/gs/unicode/utf8/utf8.js.map +1 -1
  221. package/dist/gs/unique/index.d.ts +11 -0
  222. package/dist/gs/unique/index.js +71 -0
  223. package/dist/gs/unique/index.js.map +1 -0
  224. package/go.sum +9 -0
  225. package/gs/builtin/builtin.ts +239 -8
  226. package/gs/builtin/channel.ts +22 -0
  227. package/gs/builtin/runtime-contract.test.ts +126 -0
  228. package/gs/builtin/slice.ts +259 -50
  229. package/gs/builtin/type.ts +109 -34
  230. package/gs/builtin/varRef.ts +38 -1
  231. package/gs/bytes/buffer.gs.ts +48 -44
  232. package/gs/bytes/meta.json +8 -3
  233. package/gs/bytes/reader.gs.ts +20 -19
  234. package/gs/context/context.test.ts +41 -0
  235. package/gs/context/context.ts +22 -26
  236. package/gs/crypto/internal/fips140deps/byteorder/index.ts +1 -0
  237. package/gs/crypto/internal/fips140deps/godebug/index.ts +1 -0
  238. package/gs/embed/index.ts +20 -0
  239. package/gs/embed/meta.json +5 -0
  240. package/gs/encoding/json/index.test.ts +15 -1
  241. package/gs/encoding/json/index.ts +24 -0
  242. package/gs/errors/errors.test.ts +82 -0
  243. package/gs/errors/errors.ts +104 -0
  244. package/gs/fmt/fmt.ts +56 -16
  245. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +73 -1
  246. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +297 -1
  247. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +159 -0
  248. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +1005 -0
  249. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +719 -0
  250. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +40 -0
  251. package/gs/github.com/klauspost/compress/internal/le/index.test.ts +36 -0
  252. package/gs/github.com/klauspost/compress/internal/le/index.ts +114 -0
  253. package/gs/go/internal/scannerhooks/index.test.ts +14 -0
  254. package/gs/go/internal/scannerhooks/index.ts +9 -0
  255. package/gs/go/scanner/index.test.ts +22 -0
  256. package/gs/go/scanner/index.ts +47 -0
  257. package/gs/go/token/index.test.ts +47 -1
  258. package/gs/go/token/index.ts +570 -4
  259. package/gs/internal/abi/index.test.ts +18 -0
  260. package/gs/internal/abi/index.ts +14 -0
  261. package/gs/internal/bytealg/index.test.ts +18 -0
  262. package/gs/internal/bytealg/index.ts +16 -0
  263. package/gs/internal/byteorder/index.test.ts +39 -0
  264. package/gs/internal/byteorder/index.ts +100 -27
  265. package/gs/internal/godebug/index.test.ts +16 -0
  266. package/gs/internal/godebug/index.ts +35 -0
  267. package/gs/io/fs/index.ts +1 -0
  268. package/gs/io/fs/meta.json +5 -0
  269. package/gs/io/fs/readlink.test.ts +43 -0
  270. package/gs/io/fs/readlink.ts +77 -0
  271. package/gs/io/fs/walk.test.ts +61 -0
  272. package/gs/io/fs/walk.ts +9 -9
  273. package/gs/io/io.ts +174 -31
  274. package/gs/io/meta.json +10 -2
  275. package/gs/maps/iter.ts +12 -6
  276. package/gs/maps/maps.ts +8 -6
  277. package/gs/math/bits/index.ts +103 -47
  278. package/gs/math/const.gs.test.ts +11 -5
  279. package/gs/math/const.gs.ts +5 -6
  280. package/gs/mime/index.ts +54 -0
  281. package/gs/net/http/httptest/index.ts +25 -0
  282. package/gs/net/http/index.test.ts +20 -0
  283. package/gs/net/http/index.ts +81 -0
  284. package/gs/os/dir_unix.gs.ts +2 -3
  285. package/gs/os/types_js.gs.ts +2 -2
  286. package/gs/path/filepath/match.test.ts +31 -12
  287. package/gs/path/filepath/match.ts +178 -3
  288. package/gs/path/filepath/path.test.ts +25 -0
  289. package/gs/path/filepath/path.ts +159 -5
  290. package/gs/path/path.ts +20 -5
  291. package/gs/reflect/index.ts +1 -0
  292. package/gs/reflect/map.test.ts +19 -0
  293. package/gs/reflect/map.ts +4 -0
  294. package/gs/reflect/type.ts +197 -17
  295. package/gs/runtime/debug/index.test.ts +24 -0
  296. package/gs/runtime/debug/index.ts +8 -0
  297. package/gs/runtime/runtime.test.ts +19 -0
  298. package/gs/runtime/runtime.ts +98 -3
  299. package/gs/slices/slices.test.ts +94 -0
  300. package/gs/slices/slices.ts +245 -5
  301. package/gs/sort/meta.json +7 -0
  302. package/gs/sort/slice.gs.ts +16 -7
  303. package/gs/sort/sort.gs.ts +16 -13
  304. package/gs/strings/builder.ts +4 -3
  305. package/gs/sync/atomic/type.gs.ts +13 -14
  306. package/gs/sync/meta.json +3 -1
  307. package/gs/sync/sync.test.ts +13 -1
  308. package/gs/sync/sync.ts +27 -0
  309. package/gs/syscall/constants.ts +39 -24
  310. package/gs/syscall/errors.ts +10 -0
  311. package/gs/syscall/fs.ts +195 -0
  312. package/gs/syscall/js/index.ts +458 -0
  313. package/gs/syscall/js/meta.json +4 -0
  314. package/gs/syscall/net.test.ts +85 -0
  315. package/gs/syscall/types.ts +56 -0
  316. package/gs/testing/index.ts +1 -0
  317. package/gs/testing/meta.json +5 -0
  318. package/gs/testing/testing.test.ts +90 -0
  319. package/gs/testing/testing.ts +382 -0
  320. package/gs/time/time.test.ts +106 -0
  321. package/gs/time/time.ts +278 -57
  322. package/gs/unicode/unicode.test.ts +25 -0
  323. package/gs/unicode/unicode.ts +119 -9
  324. package/gs/unicode/utf8/utf8.test.ts +13 -0
  325. package/gs/unicode/utf8/utf8.ts +28 -16
  326. package/gs/unique/index.ts +91 -0
  327. package/package.json +2 -1
@@ -5,6 +5,8 @@ import (
5
5
  "errors"
6
6
  "os"
7
7
  "path/filepath"
8
+ "regexp"
9
+ "slices"
8
10
  "strings"
9
11
  "testing"
10
12
  )
@@ -127,8 +129,8 @@ func TestCompilePackagesEmitsSimplePackage(t *testing.T) {
127
129
  "import * as $ from \"@goscript/builtin/index.js\"",
128
130
  "export const Greeting: string = \"Hello\"",
129
131
  "export function Add(a: number, b: number): number",
130
- "export async function main(): Promise<void>",
131
- "let size = $.len(Greeting)",
132
+ "export async function main(): globalThis.Promise<void>",
133
+ "let size = 5",
132
134
  "$.print(\"total:\", size)",
133
135
  "$.println(Greeting, total)",
134
136
  "$.panic(\"unreachable\")",
@@ -140,6 +142,262 @@ func TestCompilePackagesEmitsSimplePackage(t *testing.T) {
140
142
  }
141
143
  }
142
144
 
145
+ func TestCompilePackagesSkipsPureTopLevelBlankAssertions(t *testing.T) {
146
+ moduleDir := writePackageGraphFixture(t, map[string]string{
147
+ "go.mod": "module example.test/blankassert\n\ngo 1.25.3\n",
148
+ "main.go": strings.Join([]string{
149
+ "package main",
150
+ "type named interface { Name() string }",
151
+ "type item struct{}",
152
+ "func (*item) Name() string { return \"item\" }",
153
+ "var defaultItem = &item{}",
154
+ "var _ named = defaultItem",
155
+ "func main() { println(defaultItem.Name()) }",
156
+ "",
157
+ }, "\n"),
158
+ })
159
+ outputDir := filepath.Join(t.TempDir(), "output")
160
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
161
+ if err != nil {
162
+ t.Fatal(err.Error())
163
+ }
164
+
165
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
166
+ t.Fatal(err.Error())
167
+ }
168
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "blankassert", "main.gs.ts")
169
+ content, err := os.ReadFile(outputFile)
170
+ if err != nil {
171
+ t.Fatal(err.Error())
172
+ }
173
+ text := string(content)
174
+ if strings.Contains(text, "__goscriptBlank") {
175
+ t.Fatalf("pure top-level blank assertion emitted runtime binding:\n%s", text)
176
+ }
177
+ if !strings.Contains(text, "export let defaultItem") {
178
+ t.Fatalf("missing real package variable:\n%s", text)
179
+ }
180
+ }
181
+
182
+ func TestCompilePackagesLazilyInitializesCrossFilePackageVars(t *testing.T) {
183
+ moduleDir := writePackageGraphFixture(t, map[string]string{
184
+ "go.mod": "module example.test/lazyvars\n\ngo 1.25.3\n",
185
+ "a.go": strings.Join([]string{
186
+ "package main",
187
+ "type words []int",
188
+ "type remote struct { n int }",
189
+ "var one = words{1}",
190
+ "func useTwo() int { return two.values[0] + remoteZero.n }",
191
+ "",
192
+ }, "\n"),
193
+ "b.go": strings.Join([]string{
194
+ "package main",
195
+ "type holder struct { values words }",
196
+ "var two = holder{one}",
197
+ "var remoteZero remote",
198
+ "func main() { println(useTwo(), two.values[0]) }",
199
+ "",
200
+ }, "\n"),
201
+ })
202
+ outputDir := filepath.Join(t.TempDir(), "output")
203
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
204
+ if err != nil {
205
+ t.Fatal(err.Error())
206
+ }
207
+
208
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
209
+ t.Fatal(err.Error())
210
+ }
211
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazyvars", "b.gs.ts")
212
+ content, err := os.ReadFile(outputFile)
213
+ if err != nil {
214
+ t.Fatal(err.Error())
215
+ }
216
+ text := string(content)
217
+ for _, want := range []string{
218
+ "export let two: holder = undefined as unknown as holder",
219
+ "export function __goscript_get_two(): holder",
220
+ "export let remoteZero: __goscript_a.remote = undefined as unknown as __goscript_a.remote",
221
+ "export function __goscript_get_remoteZero(): __goscript_a.remote",
222
+ "__goscript_a.one",
223
+ } {
224
+ if !strings.Contains(text, want) {
225
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
226
+ }
227
+ }
228
+ aFile := filepath.Join(outputDir, "@goscript", "example.test", "lazyvars", "a.gs.ts")
229
+ aContent, err := os.ReadFile(aFile)
230
+ if err != nil {
231
+ t.Fatal(err.Error())
232
+ }
233
+ if !strings.Contains(string(aContent), "__goscript_b.__goscript_get_two()") {
234
+ t.Fatalf("missing lazy cross-file getter use in a.go output:\n%s", string(aContent))
235
+ }
236
+ }
237
+
238
+ func TestCompilePackagesLazilyInitializesFunctionBodyPackageVarDependencies(t *testing.T) {
239
+ moduleDir := writePackageGraphFixture(t, map[string]string{
240
+ "go.mod": "module example.test/lazybodyvars\n\ngo 1.25.3\n",
241
+ "main.go": strings.Join([]string{
242
+ "package main",
243
+ "type Point struct { n int }",
244
+ "var first, _ = new(Point).SetBytes()",
245
+ "func (p *Point) SetBytes() (*Point, error) {",
246
+ " p.n = later",
247
+ " return p, nil",
248
+ "}",
249
+ "var later = 7",
250
+ "func main() { println(first.n) }",
251
+ "",
252
+ }, "\n"),
253
+ })
254
+ outputDir := filepath.Join(t.TempDir(), "output")
255
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
256
+ if err != nil {
257
+ t.Fatal(err.Error())
258
+ }
259
+
260
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
261
+ t.Fatal(err.Error())
262
+ }
263
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "lazybodyvars", "main.gs.ts")
264
+ content, err := os.ReadFile(outputFile)
265
+ if err != nil {
266
+ t.Fatal(err.Error())
267
+ }
268
+ text := string(content)
269
+ for _, want := range []string{
270
+ "export let first: Point | $.VarRef<Point> | null = undefined as unknown as Point | $.VarRef<Point> | null",
271
+ "export function __goscript_get_first(): Point | $.VarRef<Point> | null",
272
+ "function __goscript_get___goscriptTuple",
273
+ "export let later: number = 7",
274
+ } {
275
+ if !strings.Contains(text, want) {
276
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
277
+ }
278
+ }
279
+ }
280
+
281
+ func TestCompilePackagesEmitsShadowedBuiltinCalls(t *testing.T) {
282
+ moduleDir := writePackageGraphFixture(t, map[string]string{
283
+ "go.mod": "module example.test/shadowbuiltin\n\ngo 1.25.3\n",
284
+ "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()",
291
+ "}",
292
+ "",
293
+ }, "\n"),
294
+ })
295
+ outputDir := filepath.Join(t.TempDir(), "output")
296
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
297
+ if err != nil {
298
+ t.Fatal(err.Error())
299
+ }
300
+
301
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
302
+ t.Fatal(err.Error())
303
+ }
304
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "shadowbuiltin", "main.gs.ts")
305
+ content, err := os.ReadFile(outputFile)
306
+ if err != nil {
307
+ t.Fatal(err.Error())
308
+ }
309
+ 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)
312
+ }
313
+ }
314
+
315
+ func TestCompilePackagesQuotesRawStringLiterals(t *testing.T) {
316
+ moduleDir := writePackageGraphFixture(t, map[string]string{
317
+ "go.mod": "module example.test/rawstrings\n\ngo 1.25.3\n",
318
+ "main.go": strings.Join([]string{
319
+ "package rawstrings",
320
+ "func Values() (string, string) {",
321
+ " return `\\u00`, `invalid escape char after \\`",
322
+ "}",
323
+ "",
324
+ }, "\n"),
325
+ })
326
+ outputDir := filepath.Join(t.TempDir(), "output")
327
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
328
+ if err != nil {
329
+ t.Fatal(err.Error())
330
+ }
331
+
332
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
333
+ t.Fatal(err.Error())
334
+ }
335
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "rawstrings", "main.gs.ts")
336
+ content, err := os.ReadFile(outputFile)
337
+ if err != nil {
338
+ t.Fatal(err.Error())
339
+ }
340
+ 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)
343
+ }
344
+ }
345
+
346
+ func TestCompilePackagesUsesEmbedOverride(t *testing.T) {
347
+ 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}),
352
+ "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",
363
+ "}",
364
+ "",
365
+ }, "\n"),
366
+ })
367
+ outputDir := filepath.Join(t.TempDir(), "output")
368
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir, AllDependencies: true}, nil, nil)
369
+ if err != nil {
370
+ t.Fatal(err.Error())
371
+ }
372
+
373
+ result, err := comp.CompilePackages(context.Background(), ".")
374
+ if err != nil {
375
+ t.Fatal(err.Error())
376
+ }
377
+ if !slices.Contains(result.CopiedPackages, "embed") {
378
+ t.Fatalf("embed override was not copied: %#v", result.CopiedPackages)
379
+ }
380
+ if _, err := os.Stat(filepath.Join(outputDir, "@goscript", "embed", "index.ts")); err != nil {
381
+ t.Fatalf("embed override missing from output: %v", err)
382
+ }
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)
385
+ }
386
+ content, err := os.ReadFile(filepath.Join(outputDir, "@goscript", "example.test", "embedblank", "main.gs.ts"))
387
+ if err != nil {
388
+ t.Fatal(err.Error())
389
+ }
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))
392
+ }
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))
395
+ }
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))
398
+ }
399
+ }
400
+
143
401
  func TestCompilePackagesEmitsPackageLocalImport(t *testing.T) {
144
402
  moduleDir := writePackageGraphFixture(t, map[string]string{
145
403
  "go.mod": "module example.test/imports\n\ngo 1.25.3\n",
@@ -226,6 +484,49 @@ func TestCompilePackagesEmitsPackageLocalImport(t *testing.T) {
226
484
  }
227
485
  }
228
486
 
487
+ func TestCompilePackagesEmitsIndexAddressRefs(t *testing.T) {
488
+ moduleDir := writePackageGraphFixture(t, map[string]string{
489
+ "go.mod": "module example.test/indexaddr\n\ngo 1.25.3\n",
490
+ "main.go": strings.Join([]string{
491
+ "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}}",
502
+ "}",
503
+ "",
504
+ }, "\n"),
505
+ })
506
+ outputDir := filepath.Join(t.TempDir(), "output")
507
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
508
+ if err != nil {
509
+ t.Fatal(err.Error())
510
+ }
511
+
512
+ _, err = comp.CompilePackages(context.Background(), ".")
513
+ if err != nil {
514
+ t.Fatal(err.Error())
515
+ }
516
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "indexaddr", "main.gs.ts")
517
+ content, err := os.ReadFile(outputFile)
518
+ if err != nil {
519
+ t.Fatal(err.Error())
520
+ }
521
+ 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)
527
+ }
528
+ }
529
+
229
530
  func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
230
531
  moduleDir := writePackageGraphFixture(t, map[string]string{
231
532
  "go.mod": "module example.test/structs\n\ngo 1.25.3\n",
@@ -241,6 +542,9 @@ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
241
542
  "func (c *Counter) Set(v int) {",
242
543
  " c.Value = v",
243
544
  "}",
545
+ "func NewCounter() *Counter {",
546
+ " return &Counter{Value: 3}",
547
+ "}",
244
548
  "func main() {",
245
549
  " original := Counter{Value: 1}",
246
550
  "",
@@ -248,6 +552,7 @@ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
248
552
  " copy := original",
249
553
  " pointer := &original",
250
554
  " pointer.Set(2)",
555
+ " NewCounter().Set(5)",
251
556
  " var iface any = pointer",
252
557
  " _, ok := iface.(*Counter)",
253
558
  " println(copy.Read(), original.Read(), ok)",
@@ -278,9 +583,10 @@ func TestCompilePackagesEmitsStructMethodsAndPointerAssertions(t *testing.T) {
278
583
  "public Set(v: number): void",
279
584
  "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))",
280
585
  "let original = $.varRef($.markAsStructValue(new Counter({Value: 1})))\n\n\t// Copy should stay readable in generated output.\n\tlet copy",
281
- "let copy = $.markAsStructValue(original.value.clone())",
282
- "let pointer = original",
283
- "$.pointerValue(pointer).Set(2)",
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)",
284
590
  "let [, ok] = $.typeAssertTuple<Counter | $.VarRef<Counter> | null>(iface, { kind: $.TypeKind.Pointer, elemType: \"main.Counter\" })",
285
591
  "\"Value\": { type: { kind: $.TypeKind.Basic, name: \"int\" }, tag: \"json:\\\"value\\\"\" }",
286
592
  } {
@@ -326,7 +632,7 @@ func TestCompilePackagesEmitsNestedPointerStorageAssertions(t *testing.T) {
326
632
  "let p1 = $.varRef(x)",
327
633
  "let p2 = $.varRef(p1)",
328
634
  "let p3 = p2",
329
- "$.pointerValue($.pointerValue(p3))!.value = 12",
635
+ "$.pointerValue<$.VarRef<number> | null>($.pointerValue<$.VarRef<$.VarRef<number> | null> | null>(p3))!.value = 12",
330
636
  } {
331
637
  if !strings.Contains(text, want) {
332
638
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -381,19 +687,19 @@ func TestCompilePackagesEmitsArraySliceMapStringAndNamedMethods(t *testing.T) {
381
687
  "export type MyInt = number",
382
688
  "export function MyInt_Double(m: MyInt): number",
383
689
  "export type MySlice = $.Slice<number>",
384
- "export function MySlice_Add(s: $.VarRef<MySlice>, v: number): void",
690
+ "export function MySlice_Add(s: $.VarRef<MySlice> | null, v: number): void",
385
691
  "let arr = [0, 10, 0]",
386
692
  "let slice = $.makeSlice<number>(0, 2, \"number\")",
387
693
  "let empty = $.arrayToSlice<number>([])",
388
694
  "let literal = $.arrayToSlice<number>([1, 2])",
389
695
  "literal = $.append(literal, 3)",
390
696
  "slice![0] = arr[1]",
391
- "let m = $.makeMap<string, number>()",
697
+ "let m: Map<string, number> | null = $.makeMap<string, number>()",
392
698
  "$.mapSet(m, \"one\", 1)",
393
699
  "let [value, ok] = $.mapGet(m, \"missing\", 0)",
394
700
  "slice![0]",
395
701
  "literal![2]",
396
- "let list: $.VarRef<MySlice> = $.varRef(null)",
702
+ "let list: $.VarRef<MySlice> = $.varRef(null as MySlice)",
397
703
  "MySlice_Add(list, 7)",
398
704
  "$.indexStringOrBytes(text, 0)",
399
705
  "MyInt_Double(5)",
@@ -452,18 +758,19 @@ func TestCompilePackagesEmitsInterfacesMethodValuesTypeSwitchesAndFunctionAssert
452
758
  }
453
759
  text := string(content)
454
760
  for _, want := range []string{
455
- "export type Greeter = ((name: string) => string) | null",
761
+ "export type Greeter = ((name: string) => string | globalThis.Promise<string>) | null",
456
762
  "export type ReadCloser = null | {",
457
763
  "Read(): string",
458
764
  "Close(): string",
459
765
  "$.registerInterfaceType(\n\t\"main.ReadCloser\"",
460
- "((__receiver) => () => __receiver.Inc())($.pointerValue(counter))",
766
+ "((__receiver) => () => __receiver.Inc())($.pointerValue<Counter>(counter))",
461
767
  "$.namedFunction(greet, \"main.Greeter\")",
462
768
  "$.typedNil(\"*struct{Name string}\")",
463
769
  "elemType: { kind: $.TypeKind.Struct, methods: [], fields: {\"Name\": { kind: $.TypeKind.Basic, name: \"string\" }} }",
464
770
  "let fn = __goscriptTuple",
465
- "$.typeSwitch(",
466
- "types: [\"main.ReadCloser\"]",
771
+ "switch (true)",
772
+ "case $.typeAssert<ReadCloser | null>(__goscriptTypeSwitchValue, \"main.ReadCloser\").ok",
773
+ "let v: ReadCloser | null = $.typeAssert<ReadCloser | null>(__goscriptTypeSwitchValue, \"main.ReadCloser\").value",
467
774
  } {
468
775
  if !strings.Contains(text, want) {
469
776
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -506,7 +813,7 @@ func TestCompilePackagesAssertsInterfaceMethodReceivers(t *testing.T) {
506
813
  text := string(content)
507
814
  for _, want := range []string{
508
815
  "export type FileInfo = null | {",
509
- "$.println(info!.Name())",
816
+ "$.println($.pointerValue<Exclude<FileInfo, null>>(info).Name())",
510
817
  } {
511
818
  if !strings.Contains(text, want) {
512
819
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -555,9 +862,9 @@ func TestCompilePackagesBoxesTypedNilInterfaceValues(t *testing.T) {
555
862
  }
556
863
  text := string(content)
557
864
  for _, want := range []string{
558
- "return $.interfaceValue<Animal>(FindDog(), \"*main.Dog\")",
559
- "$.println(animal!.Name())",
560
- "let a: Animal = $.interfaceValue<Animal>(dog, \"*main.Dog\")",
865
+ "return $.interfaceValue<Animal | null>(FindDog(), \"*main.Dog\")",
866
+ "$.println($.pointerValue<Exclude<Animal, null>>(animal).Name())",
867
+ "let a: Animal | null = $.interfaceValue<Animal | null>(dog, \"*main.Dog\")",
561
868
  } {
562
869
  if !strings.Contains(text, want) {
563
870
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -617,7 +924,7 @@ func TestCompilePackagesEmitsGenericMethodsAliasesAndDictionaries(t *testing.T)
617
924
  for _, want := range []string{
618
925
  "public Get(): any",
619
926
  "export function NewBox(__typeArgs: $.GenericTypeArgs | undefined, value: any): Box",
620
- "let seen = $.makeMap<number, Record<string, unknown>>()",
927
+ "let seen: Set = $.makeMap<number, {}>()",
621
928
  "$.mapSet(seen, 1, {})",
622
929
  "$.genericZero(__typeArgs, \"T\", null)",
623
930
  "$.callGenericMethod(__typeArgs, \"T\", \"String\", v)",
@@ -668,8 +975,8 @@ func TestCompilePackagesAttachesFunctionLiteralTypeInfo(t *testing.T) {
668
975
  }
669
976
  text := string(content)
670
977
  for _, want := range []string{
671
- "export type Callback = ((value: number) => string) | null",
672
- "export function call(cb: Callback): string {\n\treturn cb!(1)",
978
+ "export type Callback = ((value: number) => string | globalThis.Promise<string>) | null",
979
+ "export async function call(cb: ((value: number) => string | globalThis.Promise<string>) | null): globalThis.Promise<string> {\n\treturn await cb!(1)",
673
980
  "$.functionValue((value: number): string => {",
674
981
  "kind: $.TypeKind.Function",
675
982
  "params: [{ kind: $.TypeKind.Basic, name: \"int\" }]",
@@ -681,6 +988,44 @@ func TestCompilePackagesAttachesFunctionLiteralTypeInfo(t *testing.T) {
681
988
  }
682
989
  }
683
990
 
991
+ func TestCompilePackagesEmitsRecursiveFunctionTypeInfo(t *testing.T) {
992
+ moduleDir := writePackageGraphFixture(t, map[string]string{
993
+ "go.mod": "module example.test/recursive-function-type\n\ngo 1.25.3\n",
994
+ "main.go": strings.Join([]string{
995
+ "package main",
996
+ "type Handler func(Handler) Handler",
997
+ "type Holder struct { Next Handler }",
998
+ "func main() { _ = Holder{} }",
999
+ "",
1000
+ }, "\n"),
1001
+ })
1002
+ outputDir := filepath.Join(t.TempDir(), "output")
1003
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1004
+ if err != nil {
1005
+ t.Fatal(err.Error())
1006
+ }
1007
+
1008
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1009
+ t.Fatal(err.Error())
1010
+ }
1011
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "recursive-function-type", "main.gs.ts")
1012
+ content, err := os.ReadFile(outputFile)
1013
+ if err != nil {
1014
+ t.Fatal(err.Error())
1015
+ }
1016
+ text := string(content)
1017
+ 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\"",
1020
+ "params: [{ kind: $.TypeKind.Function, params: [], results: [] }]",
1021
+ "results: [{ kind: $.TypeKind.Function, params: [], results: [] }]",
1022
+ } {
1023
+ if !strings.Contains(text, want) {
1024
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1025
+ }
1026
+ }
1027
+ }
1028
+
684
1029
  func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
685
1030
  moduleDir := writePackageGraphFixture(t, map[string]string{
686
1031
  "go.mod": "module example.test/variadic\n\ngo 1.25.3\n",
@@ -734,7 +1079,7 @@ func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
734
1079
  }
735
1080
  text := string(content)
736
1081
  for _, want := range []string{
737
- "export type Collector = ((label: string, parts: $.Slice<string>) => string) | null",
1082
+ "export type Collector = ((label: string, parts: $.Slice<string>) => string | globalThis.Promise<string>) | null",
738
1083
  "Join(parts: $.Slice<string>): string",
739
1084
  "export function collect(label: string, parts: $.Slice<string>): string",
740
1085
  "let part = parts![__rangeIndex]",
@@ -746,7 +1091,7 @@ func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
746
1091
  "$.append(parts, \"c\", \"d\")",
747
1092
  "maybeErr($.arrayToSlice<string>([\"ok\"]))",
748
1093
  "fn!(\"fn\", $.arrayToSlice<string>([\"x\"]))",
749
- "joiner!.Join($.arrayToSlice<string>([\"q\", \"r\"]))",
1094
+ "$.pointerValue<Exclude<Joiner, null>>(joiner).Join($.arrayToSlice<string>([\"q\", \"r\"]))",
750
1095
  } {
751
1096
  if !strings.Contains(text, want) {
752
1097
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -754,6 +1099,46 @@ func TestCompilePackagesPacksVariadicCalls(t *testing.T) {
754
1099
  }
755
1100
  }
756
1101
 
1102
+ func TestCompilePackagesPacksVariadicCallsInGeneratedSubpackage(t *testing.T) {
1103
+ 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)",
1114
+ "}",
1115
+ "",
1116
+ }, "\n"),
1117
+ })
1118
+ outputDir := filepath.Join(t.TempDir(), "output")
1119
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1120
+ if err != nil {
1121
+ t.Fatal(err.Error())
1122
+ }
1123
+
1124
+ if _, err := comp.CompilePackages(context.Background(), "./json"); err != nil {
1125
+ t.Fatal(err.Error())
1126
+ }
1127
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "variadic-subpackage", "json", "json.gs.ts")
1128
+ content, err := os.ReadFile(outputFile)
1129
+ if err != nil {
1130
+ t.Fatal(err.Error())
1131
+ }
1132
+ 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)
1136
+ }
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)
1139
+ }
1140
+ }
1141
+
757
1142
  func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
758
1143
  moduleDir := writePackageGraphFixture(t, map[string]string{
759
1144
  "go.mod": "module example.test/iterators\n\ngo 1.25.3\n",
@@ -781,6 +1166,21 @@ func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
781
1166
  " }",
782
1167
  " println(i)",
783
1168
  " }",
1169
+ " ch := make(chan int, 1)",
1170
+ " for _, outer := range backward([]int{1, 2}) {",
1171
+ " for range backward([]int{3}) {",
1172
+ " ch <- outer",
1173
+ " }",
1174
+ " }",
1175
+ "}",
1176
+ "func backward(values []int) func(func(int, int) bool) {",
1177
+ " return func(yield func(int, int) bool) {",
1178
+ " for i := len(values) - 1; i >= 0; i-- {",
1179
+ " if !yield(i, values[i]) {",
1180
+ " return",
1181
+ " }",
1182
+ " }",
1183
+ " }",
784
1184
  "}",
785
1185
  "",
786
1186
  }, "\n"),
@@ -801,12 +1201,14 @@ func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
801
1201
  }
802
1202
  text := string(content)
803
1203
  for _, want := range []string{
804
- "if (!_yield!(i, v))",
1204
+ "if (!await _yield!(i, v))",
805
1205
  "break",
806
- "pairs!((i, v) => {",
1206
+ "await pairs!(async (i, v) => {",
807
1207
  "last = __goscriptRange",
808
1208
  "return true",
809
1209
  "continue",
1210
+ "await backward($.arrayToSlice<number>([1, 2]))!(async (__goscriptRange",
1211
+ "await backward($.arrayToSlice<number>([3]))!(async (__goscriptRange",
810
1212
  } {
811
1213
  if !strings.Contains(text, want) {
812
1214
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -814,6 +1216,103 @@ func TestCompilePackagesLowersRangeOverFunctionIterators(t *testing.T) {
814
1216
  }
815
1217
  }
816
1218
 
1219
+ func TestCompilePackagesLowersFunctionIteratorControlFlow(t *testing.T) {
1220
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1221
+ "go.mod": "module example.test/iterator-control\n\ngo 1.25.3\n",
1222
+ "main.go": strings.Join([]string{
1223
+ "package main",
1224
+ "func values(yield func(int) bool) {",
1225
+ " for i := range 4 {",
1226
+ " if !yield(i) {",
1227
+ " return",
1228
+ " }",
1229
+ " }",
1230
+ "}",
1231
+ "func first(limit int) int {",
1232
+ " j := 3",
1233
+ " for i := 0; i < j; i, j = i+1, j-1 {",
1234
+ " println(i, j)",
1235
+ " }",
1236
+ " for v := range values {",
1237
+ " if v == 0 {",
1238
+ " continue",
1239
+ " }",
1240
+ " for i := range 2 {",
1241
+ " if i == 1 {",
1242
+ " break",
1243
+ " }",
1244
+ " }",
1245
+ " if v == limit {",
1246
+ " break",
1247
+ " }",
1248
+ " switch v {",
1249
+ " case 2:",
1250
+ " return v",
1251
+ " }",
1252
+ " switch any(v).(type) {",
1253
+ " case int:",
1254
+ " if v == 3 {",
1255
+ " return v",
1256
+ " }",
1257
+ " }",
1258
+ " }",
1259
+ " return -1",
1260
+ "}",
1261
+ "func nestedReturn(limit int) int {",
1262
+ " for v := range values {",
1263
+ " for i := range values {",
1264
+ " if i == limit {",
1265
+ " return v + i",
1266
+ " }",
1267
+ " }",
1268
+ " }",
1269
+ " return -1",
1270
+ "}",
1271
+ "func main() { println(first(3), nestedReturn(2)) }",
1272
+ "",
1273
+ }, "\n"),
1274
+ })
1275
+ outputDir := filepath.Join(t.TempDir(), "output")
1276
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1277
+ if err != nil {
1278
+ t.Fatal(err.Error())
1279
+ }
1280
+
1281
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1282
+ t.Fatal(err.Error())
1283
+ }
1284
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "iterator-control", "main.gs.ts")
1285
+ content, err := os.ReadFile(outputFile)
1286
+ if err != nil {
1287
+ t.Fatal(err.Error())
1288
+ }
1289
+ text := string(content)
1290
+ for _, want := range []string{
1291
+ "let __goscriptRangeReturn",
1292
+ "let __goscriptRangeReturnValue",
1293
+ "return true",
1294
+ "return false",
1295
+ "__goscriptRangeReturnValue",
1296
+ "if (__goscriptRangeReturn",
1297
+ "return __goscriptRangeReturnValue",
1298
+ "for (let i = 0; i < j; [i, j] = [i + 1, j - 1])",
1299
+ "for (let i = 0; i < 2; i++)",
1300
+ "switch (v)",
1301
+ "const __goscriptTypeSwitchValue",
1302
+ "switch (true)",
1303
+ "case $.typeAssert<number>(__goscriptTypeSwitchValue, { kind: $.TypeKind.Basic, name: \"int\" }).ok",
1304
+ "break",
1305
+ } {
1306
+ if !strings.Contains(text, want) {
1307
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1308
+ }
1309
+ }
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)
1313
+ }
1314
+ }
1315
+
817
1316
  func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
818
1317
  moduleDir := writePackageGraphFixture(t, map[string]string{
819
1318
  "go.mod": "module example.test/async\n\ngo 1.25.3\n",
@@ -857,16 +1356,16 @@ func TestCompilePackagesEmitsAsyncChannelsSelectAndDefer(t *testing.T) {
857
1356
  }
858
1357
  text := string(content)
859
1358
  for _, want := range []string{
860
- "Process(v: number): Promise<number>",
861
- "public async Process(v: number): Promise<number>",
1359
+ "Process(v: number): globalThis.Promise<number>",
1360
+ "public async Process(v: number): globalThis.Promise<number>",
862
1361
  "let ch = $.makeChannel<number>(1, 0, \"both\")",
863
- "await $.chanSend($.pointerValue(w).ch, v)",
864
- "return await $.chanRecv($.pointerValue(w).ch)",
1362
+ "await $.chanSend($.pointerValue<Worker>(w).ch, v)",
1363
+ "return await $.chanRecv($.pointerValue<Worker>(w).ch)",
865
1364
  "await using __defer = new $.AsyncDisposableStack()",
866
- "queueMicrotask(async () => { await ($.functionValue(async (): Promise<void> => {",
1365
+ "queueMicrotask(async () => { await ($.functionValue(async (): globalThis.Promise<void> => {",
867
1366
  "$.selectStatement<any, void>([",
868
1367
  "let v = result.value",
869
- "await call(new Worker({ch: $.makeChannel<number>(1, 0, \"both\")}))",
1368
+ "await call($.interfaceValue<Processor | null>(new Worker({ch: $.makeChannel<number>(1, 0, \"both\")}), \"*main.Worker\"))",
870
1369
  } {
871
1370
  if !strings.Contains(text, want) {
872
1371
  t.Fatalf("missing %q in generated output:\n%s", want, text)
@@ -931,6 +1430,21 @@ func TestCompilePackagesLowersSwitchesAndFunctionValueCalls(t *testing.T) {
931
1430
  " case value > 1:",
932
1431
  " println(\"positive\")",
933
1432
  " }",
1433
+ "Block:",
1434
+ " for value > 0 {",
1435
+ " switch value {",
1436
+ " case 2:",
1437
+ " value--",
1438
+ " fallthrough",
1439
+ " case 1:",
1440
+ " break Block",
1441
+ " }",
1442
+ " }",
1443
+ "Again:",
1444
+ " value--",
1445
+ " if value > 0 {",
1446
+ " goto Again",
1447
+ " }",
934
1448
  " release := func() { println(\"release\") }",
935
1449
  " rel := &release",
936
1450
  " (*rel)()",
@@ -963,7 +1477,11 @@ func TestCompilePackagesLowersSwitchesAndFunctionValueCalls(t *testing.T) {
963
1477
  "case 3:",
964
1478
  "let local = \"two-three\"",
965
1479
  "switch (true) {",
966
- "($.pointerValue(rel))!()",
1480
+ "Block: while (value > 0)",
1481
+ "break Block",
1482
+ "Again: while (true)",
1483
+ "continue Again",
1484
+ "($.pointerValue<(() => void) | null>(rel))!()",
967
1485
  "$.functionValue((): void => {\n\t\tusing __defer = new $.DisposableStack()",
968
1486
  "__defer.defer(() => { $.println(\"wrapped deferred\") })",
969
1487
  "$.println(\"wrapped body\")",
@@ -975,6 +1493,9 @@ func TestCompilePackagesLowersSwitchesAndFunctionValueCalls(t *testing.T) {
975
1493
  if strings.Count(text, "\t\tbreak\n") < 3 {
976
1494
  t.Fatalf("switch cases were not rendered with implicit breaks:\n%s", text)
977
1495
  }
1496
+ if strings.Contains(text, "fallthrough") {
1497
+ t.Fatalf("fallthrough marker leaked into generated output:\n%s", text)
1498
+ }
978
1499
  }
979
1500
 
980
1501
  func TestCompilePackagesLowersMethodValuesWithFixedParameters(t *testing.T) {
@@ -1077,11 +1598,11 @@ func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testi
1077
1598
  for _, want := range []string{
1078
1599
  "Box: $.VarRef<lib.Box>",
1079
1600
  "Boxes: $.VarRef<$.Slice<lib.Box>>",
1080
- "Fn: $.VarRef<((_p0: lib.Box) => [lib.Box, $.GoError]) | null>",
1601
+ "Fn: $.VarRef<((_p0: lib.Box) => [lib.Box, $.GoError] | globalThis.Promise<[lib.Box, $.GoError]>) | null>",
1081
1602
  "Ptr: $.VarRef<atomic.Pointer<(() => void) | null>>",
1082
1603
  "$.markAsStructValue(new lib.Box())",
1083
1604
  "$.markAsStructValue(new atomic.Pointer<(() => void) | null>())",
1084
- "export function Use(fn: ((_p0: lib.Box) => [lib.Box, $.GoError]) | null, box: lib.Box): [lib.Box, $.GoError]",
1605
+ "export async function Use(fn: ((_p0: lib.Box) => [lib.Box, $.GoError] | globalThis.Promise<[lib.Box, $.GoError]>) | null, box: lib.Box): globalThis.Promise<[lib.Box, $.GoError]>",
1085
1606
  "$.functionValue((box: lib.Box): [lib.Box, $.GoError] => {",
1086
1607
  } {
1087
1608
  if !strings.Contains(text, want) {
@@ -1090,14 +1611,16 @@ func TestCompilePackagesQualifiesImportedTypesInSignaturesAndZeroValues(t *testi
1090
1611
  }
1091
1612
  }
1092
1613
 
1093
- func TestCompilePackagesReportsUnsupportedUnaryBeforeOutput(t *testing.T) {
1614
+ func TestCompilePackagesLowersUnaryBitwiseComplement(t *testing.T) {
1094
1615
  moduleDir := writePackageGraphFixture(t, map[string]string{
1095
- "go.mod": "module example.test/unsupported\n\ngo 1.25.3\n",
1616
+ "go.mod": "module example.test/unary-bitwise\n\ngo 1.25.3\n",
1096
1617
  "main.go": strings.Join([]string{
1097
1618
  "package main",
1098
1619
  "var value = 1",
1099
1620
  "func main() {",
1100
- " println(^value)",
1621
+ " mask := 7",
1622
+ " mask &^= 3",
1623
+ " println(^value, value &^ 3, mask, 0700)",
1101
1624
  "}",
1102
1625
  "",
1103
1626
  }, "\n"),
@@ -1108,25 +1631,132 @@ func TestCompilePackagesReportsUnsupportedUnaryBeforeOutput(t *testing.T) {
1108
1631
  t.Fatal(err.Error())
1109
1632
  }
1110
1633
 
1111
- result, err := comp.CompilePackages(context.Background(), ".")
1112
- if err == nil {
1113
- t.Fatal("expected unsupported unary expression to fail")
1634
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1635
+ t.Fatal(err.Error())
1114
1636
  }
1115
- requireDiagnostic(t, err, "goscript/lowering:unsupported")
1116
- if result == nil || len(result.Diagnostics) == 0 {
1117
- t.Fatalf("expected structured diagnostics in result")
1637
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "unary-bitwise", "main.gs.ts")
1638
+ content, err := os.ReadFile(outputFile)
1639
+ if err != nil {
1640
+ t.Fatal(err.Error())
1118
1641
  }
1119
- if _, statErr := os.Stat(outputDir); !os.IsNotExist(statErr) {
1120
- t.Fatalf("compile wrote output directory after lowering failed: %v", statErr)
1642
+ text := string(content)
1643
+ for _, want := range []string{
1644
+ "mask = mask & ~(3)",
1645
+ "$.println(~value, value & ~(3), mask, 0o700)",
1646
+ } {
1647
+ if !strings.Contains(text, want) {
1648
+ t.Fatalf("missing %q in generated output:\n%s", want, text)
1649
+ }
1121
1650
  }
1122
1651
  }
1123
1652
 
1124
- func TestCompileSourceToTypeScriptStopsAtSkeleton(t *testing.T) {
1125
- _, err := CompileSourceToTypeScript("package main\nfunc main() {}\n", "main")
1126
- if err == nil {
1127
- t.Fatal("expected WASM source compile to fail in the skeleton")
1653
+ func TestCompileSourceToTypeScriptCompilesSingleFile(t *testing.T) {
1654
+ output, err := CompileSourceToTypeScript("package main\nfunc main() { println(\"hi\") }\n", "main")
1655
+ if err != nil {
1656
+ t.Fatal(err.Error())
1657
+ }
1658
+ if !strings.Contains(output, "$.println(\"hi\")") {
1659
+ t.Fatalf("missing println in generated output:\n%s", output)
1660
+ }
1661
+ }
1662
+
1663
+ func TestTypeScriptEmitOwnerEmitsToMemoryOnDiskPath(t *testing.T) {
1664
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1665
+ "go.mod": "module example.test/memoryemit\n\ngo 1.25.3\n",
1666
+ "main.go": "package main\nfunc main() { println(\"memory\") }\n",
1667
+ })
1668
+ outputDir := filepath.Join(t.TempDir(), "output")
1669
+ req := &CompileRequest{
1670
+ Patterns: []string{"."},
1671
+ Dir: moduleDir,
1672
+ OutputPath: outputDir,
1673
+ DependencyMode: DependencyModeRequested,
1674
+ RuntimeEmissionMode: RuntimeEmissionModeReference,
1675
+ }
1676
+ service := NewCompileService()
1677
+ graph, diagnostics := service.PackageGraphOwner().Load(context.Background(), req)
1678
+ if diagnosticsHaveErrors(diagnostics) {
1679
+ t.Fatalf("graph diagnostics: %#v", diagnostics)
1680
+ }
1681
+ model, diagnostics := service.SemanticModelOwner().Build(context.Background(), graph)
1682
+ if diagnosticsHaveErrors(diagnostics) {
1683
+ t.Fatalf("semantic diagnostics: %#v", diagnostics)
1684
+ }
1685
+ program, diagnostics := service.LoweringOwner().Build(context.Background(), model)
1686
+ if diagnosticsHaveErrors(diagnostics) {
1687
+ t.Fatalf("lowering diagnostics: %#v", diagnostics)
1688
+ }
1689
+
1690
+ files, diagnostics := service.TypeScriptEmitOwner().EmitToMemory(context.Background(), program)
1691
+ if diagnosticsHaveErrors(diagnostics) {
1692
+ t.Fatalf("memory emit diagnostics: %#v", diagnostics)
1693
+ }
1694
+ path := "@goscript/example.test/memoryemit/main.gs.ts"
1695
+ if !strings.Contains(files[path], "$.println(\"memory\")") {
1696
+ t.Fatalf("missing in-memory output: %#v", files)
1697
+ }
1698
+ if _, diagnostics := service.TypeScriptEmitOwner().Emit(context.Background(), req, program); diagnosticsHaveErrors(diagnostics) {
1699
+ t.Fatalf("disk emit diagnostics: %#v", diagnostics)
1700
+ }
1701
+ content, err := os.ReadFile(filepath.Join(outputDir, filepath.FromSlash(path)))
1702
+ if err != nil {
1703
+ t.Fatal(err.Error())
1704
+ }
1705
+ if string(content) != files[path] {
1706
+ t.Fatalf("disk and memory emit diverged:\n%s\n---\n%s", string(content), files[path])
1707
+ }
1708
+ }
1709
+
1710
+ func TestCompilePackagesLowersNamedStructConversionWithTypedAsyncFact(t *testing.T) {
1711
+ moduleDir := writePackageGraphFixture(t, map[string]string{
1712
+ "go.mod": "module example.test/namedstructconvert\n\ngo 1.25.3\n",
1713
+ "main.go": strings.Join([]string{
1714
+ "package main",
1715
+ "type Source struct { Value int }",
1716
+ "type Target Source",
1717
+ "func Make() Source {",
1718
+ " ch := make(chan Source, 1)",
1719
+ " ch <- Source{Value: 7}",
1720
+ " return <-ch",
1721
+ "}",
1722
+ "func Convert() Target {",
1723
+ " return Target(Make())",
1724
+ "}",
1725
+ "func ConvertLiteral() Target {",
1726
+ " return Target(func() Source {",
1727
+ " ch := make(chan Source, 1)",
1728
+ " ch <- Source{Value: 9}",
1729
+ " return <-ch",
1730
+ " }())",
1731
+ "}",
1732
+ "func main() { println(Convert().Value) }",
1733
+ "",
1734
+ }, "\n"),
1735
+ })
1736
+ outputDir := filepath.Join(t.TempDir(), "output")
1737
+ comp, err := NewCompiler(&Config{Dir: moduleDir, OutputPath: outputDir}, nil, nil)
1738
+ if err != nil {
1739
+ t.Fatal(err.Error())
1740
+ }
1741
+
1742
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
1743
+ t.Fatal(err.Error())
1744
+ }
1745
+ outputFile := filepath.Join(outputDir, "@goscript", "example.test", "namedstructconvert", "main.gs.ts")
1746
+ content, err := os.ReadFile(outputFile)
1747
+ if err != nil {
1748
+ t.Fatal(err.Error())
1749
+ }
1750
+ text := string(content)
1751
+ if !strings.Contains(text, "await (async () => { const __goscriptConvert") {
1752
+ t.Fatalf("missing async named struct conversion:\n%s", text)
1753
+ }
1754
+ if !strings.Contains(text, "$.markAsStructValue(new Target({Value: __goscriptConvert") {
1755
+ t.Fatalf("missing typed named struct conversion target:\n%s", text)
1756
+ }
1757
+ if !strings.Contains(text, "const __goscriptConvert1 = await ($.functionValue(async ") {
1758
+ t.Fatalf("missing async fact from function literal conversion source:\n%s", text)
1128
1759
  }
1129
- requireDiagnostic(t, err, "goscript/wasm:single-file-unsupported")
1130
1760
  }
1131
1761
 
1132
1762
  func requireDiagnostic(t *testing.T, err error, code string) {