goscript 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/README.md +5 -2
  2. package/cmd/go_js_wasm_exec/main.go +201 -0
  3. package/cmd/go_js_wasm_exec/main_test.go +83 -0
  4. package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +35 -8
  5. package/cmd/goscript/cmd-test.go +14 -0
  6. package/cmd/goscript/cmd-test_test.go +1 -1
  7. package/cmd/goscript/cmd_compile_test.go +105 -6
  8. package/compiler/build-flags.go +9 -10
  9. package/compiler/compile-request.go +12 -9
  10. package/compiler/compliance_test.go +0 -1
  11. package/compiler/config.go +2 -0
  12. package/compiler/gotest/request.go +28 -0
  13. package/compiler/gotest/runner.go +353 -27
  14. package/compiler/gotest/runner_test.go +400 -1
  15. package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
  16. package/compiler/gotest/testdata/browserapi/go.mod +3 -0
  17. package/compiler/lowered-program.go +24 -17
  18. package/compiler/lowering.go +988 -263
  19. package/compiler/lowering_bench_test.go +364 -0
  20. package/compiler/override-facts.go +15 -0
  21. package/compiler/override-parity-verifier.go +450 -0
  22. package/compiler/override-parity.go +122 -0
  23. package/compiler/override-registry_test.go +559 -0
  24. package/compiler/package-graph.go +61 -4
  25. package/compiler/package-graph_test.go +30 -0
  26. package/compiler/protobuf-ts-binding.go +514 -0
  27. package/compiler/protobuf-ts-binding_test.go +172 -0
  28. package/compiler/semantic-model-types.go +17 -4
  29. package/compiler/semantic-model.go +709 -72
  30. package/compiler/semantic-model_test.go +219 -0
  31. package/compiler/service.go +20 -1
  32. package/compiler/skeleton_test.go +1008 -20
  33. package/compiler/typescript-emitter.go +147 -15
  34. package/dist/gs/builtin/builtin.d.ts +2 -2
  35. package/dist/gs/builtin/builtin.js +20 -0
  36. package/dist/gs/builtin/builtin.js.map +1 -1
  37. package/dist/gs/builtin/slice.d.ts +2 -1
  38. package/dist/gs/builtin/slice.js +34 -4
  39. package/dist/gs/builtin/slice.js.map +1 -1
  40. package/dist/gs/builtin/type.d.ts +14 -6
  41. package/dist/gs/builtin/type.js +224 -64
  42. package/dist/gs/builtin/type.js.map +1 -1
  43. package/dist/gs/builtin/varRef.d.ts +11 -0
  44. package/dist/gs/builtin/varRef.js +57 -2
  45. package/dist/gs/builtin/varRef.js.map +1 -1
  46. package/dist/gs/bytes/buffer.gs.js +1 -1
  47. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  48. package/dist/gs/bytes/reader.gs.js +1 -1
  49. package/dist/gs/bytes/reader.gs.js.map +1 -1
  50. package/dist/gs/compress/zlib/index.d.ts +13 -6
  51. package/dist/gs/compress/zlib/index.js +131 -35
  52. package/dist/gs/compress/zlib/index.js.map +1 -1
  53. package/dist/gs/crypto/sha1/index.js +2 -5
  54. package/dist/gs/crypto/sha1/index.js.map +1 -1
  55. package/dist/gs/crypto/sha256/index.js +2 -5
  56. package/dist/gs/crypto/sha256/index.js.map +1 -1
  57. package/dist/gs/crypto/sha512/index.js +2 -5
  58. package/dist/gs/crypto/sha512/index.js.map +1 -1
  59. package/dist/gs/embed/index.d.ts +6 -0
  60. package/dist/gs/embed/index.js +210 -5
  61. package/dist/gs/embed/index.js.map +1 -1
  62. package/dist/gs/encoding/json/index.d.ts +114 -0
  63. package/dist/gs/encoding/json/index.js +544 -36
  64. package/dist/gs/encoding/json/index.js.map +1 -1
  65. package/dist/gs/fmt/fmt.d.ts +3 -3
  66. package/dist/gs/fmt/fmt.js +29 -16
  67. package/dist/gs/fmt/fmt.js.map +1 -1
  68. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +100 -0
  69. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +564 -0
  70. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  71. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.d.ts +45 -0
  72. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js +229 -0
  73. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js.map +1 -0
  74. package/dist/gs/github.com/pkg/errors/errors.js +54 -30
  75. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  76. package/dist/gs/go/scanner/index.d.ts +2 -0
  77. package/dist/gs/go/scanner/index.js +29 -5
  78. package/dist/gs/go/scanner/index.js.map +1 -1
  79. package/dist/gs/go/token/index.js +22 -6
  80. package/dist/gs/go/token/index.js.map +1 -1
  81. package/dist/gs/hash/index.d.ts +6 -0
  82. package/dist/gs/hash/index.js +20 -0
  83. package/dist/gs/hash/index.js.map +1 -1
  84. package/dist/gs/internal/goarch/index.d.ts +43 -3
  85. package/dist/gs/internal/goarch/index.js +42 -10
  86. package/dist/gs/internal/goarch/index.js.map +1 -1
  87. package/dist/gs/io/fs/fs.js +26 -14
  88. package/dist/gs/io/fs/fs.js.map +1 -1
  89. package/dist/gs/io/fs/readdir.js +8 -4
  90. package/dist/gs/io/fs/readdir.js.map +1 -1
  91. package/dist/gs/io/fs/sub.js +8 -1
  92. package/dist/gs/io/fs/sub.js.map +1 -1
  93. package/dist/gs/io/io.d.ts +12 -6
  94. package/dist/gs/io/io.js +87 -42
  95. package/dist/gs/io/io.js.map +1 -1
  96. package/dist/gs/math/bits/index.d.ts +31 -5
  97. package/dist/gs/math/bits/index.js +29 -28
  98. package/dist/gs/math/bits/index.js.map +1 -1
  99. package/dist/gs/mime/index.d.ts +16 -0
  100. package/dist/gs/mime/index.js +315 -6
  101. package/dist/gs/mime/index.js.map +1 -1
  102. package/dist/gs/net/http/httptest/index.d.ts +12 -0
  103. package/dist/gs/net/http/httptest/index.js +85 -6
  104. package/dist/gs/net/http/httptest/index.js.map +1 -1
  105. package/dist/gs/net/http/index.d.ts +303 -6
  106. package/dist/gs/net/http/index.js +1615 -58
  107. package/dist/gs/net/http/index.js.map +1 -1
  108. package/dist/gs/os/dir_unix.gs.js +1 -1
  109. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  110. package/dist/gs/os/error.gs.js +1 -1
  111. package/dist/gs/os/error.gs.js.map +1 -1
  112. package/dist/gs/os/exec.gs.d.ts +1 -0
  113. package/dist/gs/os/exec.gs.js +4 -8
  114. package/dist/gs/os/exec.gs.js.map +1 -1
  115. package/dist/gs/os/exec_posix.gs.js +1 -1
  116. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  117. package/dist/gs/os/index.d.ts +1 -1
  118. package/dist/gs/os/index.js +1 -1
  119. package/dist/gs/os/index.js.map +1 -1
  120. package/dist/gs/os/proc.gs.d.ts +4 -0
  121. package/dist/gs/os/proc.gs.js +12 -6
  122. package/dist/gs/os/proc.gs.js.map +1 -1
  123. package/dist/gs/os/root_js.gs.js +1 -1
  124. package/dist/gs/os/root_js.gs.js.map +1 -1
  125. package/dist/gs/os/types.gs.js +1 -1
  126. package/dist/gs/os/types.gs.js.map +1 -1
  127. package/dist/gs/os/types_js.gs.d.ts +6 -2
  128. package/dist/gs/os/types_js.gs.js +170 -9
  129. package/dist/gs/os/types_js.gs.js.map +1 -1
  130. package/dist/gs/os/types_unix.gs.js +1 -1
  131. package/dist/gs/os/types_unix.gs.js.map +1 -1
  132. package/dist/gs/path/path.js +11 -7
  133. package/dist/gs/path/path.js.map +1 -1
  134. package/dist/gs/reflect/index.d.ts +5 -4
  135. package/dist/gs/reflect/index.js +4 -3
  136. package/dist/gs/reflect/index.js.map +1 -1
  137. package/dist/gs/reflect/map.js +15 -0
  138. package/dist/gs/reflect/map.js.map +1 -1
  139. package/dist/gs/reflect/type.d.ts +26 -6
  140. package/dist/gs/reflect/type.js +1498 -279
  141. package/dist/gs/reflect/type.js.map +1 -1
  142. package/dist/gs/reflect/types.d.ts +14 -6
  143. package/dist/gs/reflect/types.js +35 -1
  144. package/dist/gs/reflect/types.js.map +1 -1
  145. package/dist/gs/reflect/value.d.ts +1 -0
  146. package/dist/gs/reflect/value.js +83 -41
  147. package/dist/gs/reflect/value.js.map +1 -1
  148. package/dist/gs/reflect/visiblefields.js +4 -140
  149. package/dist/gs/reflect/visiblefields.js.map +1 -1
  150. package/dist/gs/runtime/pprof/index.d.ts +8 -2
  151. package/dist/gs/runtime/pprof/index.js +50 -30
  152. package/dist/gs/runtime/pprof/index.js.map +1 -1
  153. package/dist/gs/runtime/runtime.js +5 -4
  154. package/dist/gs/runtime/runtime.js.map +1 -1
  155. package/dist/gs/runtime/trace/index.js +5 -19
  156. package/dist/gs/runtime/trace/index.js.map +1 -1
  157. package/dist/gs/strconv/atoi.gs.js +1 -1
  158. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  159. package/dist/gs/strconv/complex.gs.d.ts +3 -0
  160. package/dist/gs/strconv/complex.gs.js +148 -0
  161. package/dist/gs/strconv/complex.gs.js.map +1 -0
  162. package/dist/gs/strconv/index.d.ts +1 -0
  163. package/dist/gs/strconv/index.js +1 -0
  164. package/dist/gs/strconv/index.js.map +1 -1
  165. package/dist/gs/strings/builder.js +1 -1
  166. package/dist/gs/strings/reader.d.ts +1 -1
  167. package/dist/gs/strings/reader.js +11 -7
  168. package/dist/gs/strings/reader.js.map +1 -1
  169. package/dist/gs/strings/replace.js +15 -7
  170. package/dist/gs/strings/replace.js.map +1 -1
  171. package/dist/gs/strings/strings.d.ts +5 -0
  172. package/dist/gs/strings/strings.js +57 -5
  173. package/dist/gs/strings/strings.js.map +1 -1
  174. package/dist/gs/sync/atomic/type.gs.js +9 -9
  175. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  176. package/dist/gs/sync/atomic/value.gs.js +2 -2
  177. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  178. package/dist/gs/sync/sync.d.ts +2 -1
  179. package/dist/gs/sync/sync.js +37 -16
  180. package/dist/gs/sync/sync.js.map +1 -1
  181. package/dist/gs/syscall/env.js +22 -14
  182. package/dist/gs/syscall/env.js.map +1 -1
  183. package/dist/gs/syscall/js/index.js +9 -0
  184. package/dist/gs/syscall/js/index.js.map +1 -1
  185. package/dist/gs/testing/testing.js +59 -15
  186. package/dist/gs/testing/testing.js.map +1 -1
  187. package/dist/gs/time/time.d.ts +24 -1
  188. package/dist/gs/time/time.js +43 -3
  189. package/dist/gs/time/time.js.map +1 -1
  190. package/dist/gs/unique/index.js +7 -1
  191. package/dist/gs/unique/index.js.map +1 -1
  192. package/go.mod +3 -3
  193. package/go.sum +16 -0
  194. package/gs/builtin/builtin.ts +25 -2
  195. package/gs/builtin/runtime-contract.test.ts +260 -18
  196. package/gs/builtin/slice.ts +51 -4
  197. package/gs/builtin/type.ts +310 -63
  198. package/gs/builtin/varRef.ts +85 -2
  199. package/gs/bytes/buffer.gs.ts +1 -1
  200. package/gs/bytes/reader.gs.ts +1 -1
  201. package/gs/compress/zlib/index.test.ts +159 -1
  202. package/gs/compress/zlib/index.ts +164 -37
  203. package/gs/compress/zlib/meta.json +4 -1
  204. package/gs/compress/zlib/parity.json +51 -0
  205. package/gs/crypto/sha1/index.test.ts +19 -2
  206. package/gs/crypto/sha1/index.ts +3 -6
  207. package/gs/crypto/sha256/index.test.ts +14 -2
  208. package/gs/crypto/sha256/index.ts +3 -6
  209. package/gs/crypto/sha512/index.test.ts +17 -2
  210. package/gs/crypto/sha512/index.ts +3 -6
  211. package/gs/embed/index.test.ts +87 -0
  212. package/gs/embed/index.ts +229 -5
  213. package/gs/encoding/json/index.test.ts +360 -6
  214. package/gs/encoding/json/index.ts +679 -38
  215. package/gs/encoding/json/parity.json +81 -0
  216. package/gs/fmt/fmt.test.ts +41 -3
  217. package/gs/fmt/fmt.ts +40 -17
  218. package/gs/fmt/meta.json +6 -1
  219. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +211 -3
  220. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +857 -1
  221. package/gs/github.com/go-git/go-billy/v6/osfs/index.test.ts +110 -0
  222. package/gs/github.com/go-git/go-billy/v6/osfs/index.ts +280 -0
  223. package/gs/github.com/go-git/go-billy/v6/osfs/meta.json +8 -0
  224. package/gs/github.com/pkg/errors/errors.ts +54 -30
  225. package/gs/go/scanner/index.test.ts +39 -56
  226. package/gs/go/scanner/index.ts +33 -5
  227. package/gs/go/scanner/parity.json +27 -0
  228. package/gs/go/token/index.ts +22 -6
  229. package/gs/hash/index.test.ts +20 -33
  230. package/gs/hash/index.ts +28 -0
  231. package/gs/hash/parity.json +21 -0
  232. package/gs/internal/goarch/index.test.ts +32 -0
  233. package/gs/internal/goarch/index.ts +45 -13
  234. package/gs/internal/goarch/parity.json +144 -0
  235. package/gs/io/fs/fs.ts +26 -14
  236. package/gs/io/fs/readdir.test.ts +38 -0
  237. package/gs/io/fs/readdir.ts +8 -4
  238. package/gs/io/fs/sub.ts +8 -1
  239. package/gs/io/io.test.ts +77 -6
  240. package/gs/io/io.ts +115 -52
  241. package/gs/io/meta.json +7 -1
  242. package/gs/io/parity.json +162 -0
  243. package/gs/math/bits/index.test.ts +14 -1
  244. package/gs/math/bits/index.ts +75 -32
  245. package/gs/math/bits/parity.json +156 -0
  246. package/gs/mime/index.test.ts +90 -0
  247. package/gs/mime/index.ts +369 -6
  248. package/gs/mime/parity.json +36 -0
  249. package/gs/net/http/httptest/index.test.ts +98 -2
  250. package/gs/net/http/httptest/index.ts +101 -6
  251. package/gs/net/http/httptest/parity.json +15 -0
  252. package/gs/net/http/index.test.ts +797 -12
  253. package/gs/net/http/index.ts +1874 -136
  254. package/gs/net/http/meta.json +16 -1
  255. package/gs/net/http/parity.json +193 -0
  256. package/gs/os/dir_unix.gs.ts +1 -1
  257. package/gs/os/error.gs.ts +1 -1
  258. package/gs/os/exec.gs.ts +4 -8
  259. package/gs/os/exec_posix.gs.ts +1 -1
  260. package/gs/os/file_unix_js.test.ts +52 -0
  261. package/gs/os/index.test.ts +9 -0
  262. package/gs/os/index.ts +1 -0
  263. package/gs/os/meta.json +4 -0
  264. package/gs/os/parity.json +9 -0
  265. package/gs/os/proc.gs.ts +18 -5
  266. package/gs/os/proc.test.ts +26 -0
  267. package/gs/os/readdir.test.ts +56 -0
  268. package/gs/os/root_js.gs.ts +1 -1
  269. package/gs/os/types.gs.ts +1 -1
  270. package/gs/os/types_js.gs.ts +170 -9
  271. package/gs/os/types_unix.gs.ts +1 -1
  272. package/gs/path/path.ts +11 -7
  273. package/gs/reflect/deepequal.test.ts +10 -1
  274. package/gs/reflect/field.test.ts +37 -15
  275. package/gs/reflect/function-types.test.ts +518 -22
  276. package/gs/reflect/index.ts +8 -6
  277. package/gs/reflect/map.ts +20 -0
  278. package/gs/reflect/meta.json +6 -4
  279. package/gs/reflect/parity.json +234 -0
  280. package/gs/reflect/sliceat.test.ts +156 -0
  281. package/gs/reflect/structof.test.ts +401 -0
  282. package/gs/reflect/type.ts +1980 -365
  283. package/gs/reflect/typefor.test.ts +540 -10
  284. package/gs/reflect/types.ts +43 -18
  285. package/gs/reflect/value.ts +105 -45
  286. package/gs/reflect/visiblefields.ts +5 -168
  287. package/gs/runtime/parity.json +24 -0
  288. package/gs/runtime/pprof/index.test.ts +29 -7
  289. package/gs/runtime/pprof/index.ts +56 -30
  290. package/gs/runtime/pprof/parity.json +27 -0
  291. package/gs/runtime/runtime.test.ts +3 -1
  292. package/gs/runtime/runtime.ts +4 -3
  293. package/gs/runtime/trace/index.test.ts +5 -3
  294. package/gs/runtime/trace/index.ts +8 -20
  295. package/gs/runtime/trace/parity.json +36 -0
  296. package/gs/strconv/atoi.gs.ts +1 -1
  297. package/gs/strconv/complex.gs.ts +174 -0
  298. package/gs/strconv/complex.test.ts +65 -0
  299. package/gs/strconv/index.ts +1 -0
  300. package/gs/strconv/parity.json +120 -0
  301. package/gs/strings/builder.ts +1 -1
  302. package/gs/strings/meta.json +5 -2
  303. package/gs/strings/parity.json +186 -0
  304. package/gs/strings/reader.test.ts +2 -2
  305. package/gs/strings/reader.ts +11 -7
  306. package/gs/strings/replace.ts +15 -7
  307. package/gs/strings/strings.test.ts +22 -2
  308. package/gs/strings/strings.ts +64 -6
  309. package/gs/sync/atomic/type.gs.ts +9 -9
  310. package/gs/sync/atomic/value.gs.ts +2 -2
  311. package/gs/sync/meta.json +1 -0
  312. package/gs/sync/sync.test.ts +41 -1
  313. package/gs/sync/sync.ts +41 -16
  314. package/gs/syscall/env.ts +29 -14
  315. package/gs/syscall/js/index.test.ts +18 -0
  316. package/gs/syscall/js/index.ts +12 -0
  317. package/gs/testing/testing.test.ts +99 -3
  318. package/gs/testing/testing.ts +95 -24
  319. package/gs/time/parity.json +225 -0
  320. package/gs/time/time.test.ts +20 -2
  321. package/gs/time/time.ts +49 -7
  322. package/gs/unique/index.ts +7 -1
  323. package/package.json +4 -2
  324. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
  325. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -814
  326. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
  327. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -31
  328. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1233
  329. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
  330. /package/compiler/{wasm_api.go → wasm-api.go} +0 -0
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  <p>
7
7
  Compile Go packages into TypeScript modules for shared application logic.<br/>
8
- Built for Bun, modern bundlers, browser demos, and package-level workflows.<br/>
8
+ Performance-optimized for fast package builds, Bun, modern bundlers, and browser demos.<br/>
9
9
  </p>
10
10
 
11
11
  <p>
@@ -29,7 +29,10 @@ GoScript compiles Go packages from inside a Go module and writes deterministic
29
29
  TypeScript packages under `@goscript/<go-package>/`. The compiler keeps package
30
30
  loading, semantic modeling, lowering, TypeScript emission, runtime helpers, and
31
31
  handwritten override packages as explicit pipeline stages so generated output is
32
- readable and semantic decisions have one owner.
32
+ readable and semantic decisions have one owner. The compiler is also
33
+ performance-tuned through real package dogfooding, so large package sets can be
34
+ rebuilt quickly instead of treating Go-to-TypeScript generation as an offline
35
+ batch step.
33
36
 
34
37
  > "Right now goscript looks pretty cool if [your] problem is 'I want this
35
38
  > self-sufficient algorithm [to] be available in Go and JS runtimes.'"
@@ -0,0 +1,201 @@
1
+ package main
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "io"
7
+ "os"
8
+ "strconv"
9
+ "strings"
10
+ "time"
11
+
12
+ "github.com/aperturerobotics/goscript/compiler/gotest"
13
+ "github.com/pkg/errors"
14
+ )
15
+
16
+ func main() {
17
+ if err := run(context.Background(), os.Args[1:], os.Stdout); err != nil {
18
+ _, _ = fmt.Fprintln(os.Stderr, err)
19
+ os.Exit(1)
20
+ }
21
+ }
22
+
23
+ func run(ctx context.Context, args []string, stdout io.Writer) error {
24
+ req, err := requestFromGoToolArgs(args)
25
+ if err != nil {
26
+ return err
27
+ }
28
+ result, err := gotest.NewRunner().Run(ctx, req)
29
+ if err != nil {
30
+ return err
31
+ }
32
+ if err := printResult(stdout, result); err != nil {
33
+ return err
34
+ }
35
+ if !result.Passed() {
36
+ return errors.New("goscript browser test failed")
37
+ }
38
+ return nil
39
+ }
40
+
41
+ func requestFromGoToolArgs(args []string) (*gotest.Request, error) {
42
+ if len(args) != 0 && !strings.HasPrefix(args[0], "-") {
43
+ args = args[1:]
44
+ }
45
+ cwd, err := os.Getwd()
46
+ if err != nil {
47
+ return nil, errors.Wrap(err, "get package working directory")
48
+ }
49
+ req := &gotest.Request{
50
+ Dir: cwd,
51
+ Patterns: []string{"."},
52
+ Count: 1,
53
+ Timeout: 10 * time.Minute,
54
+ RuntimeBackend: gotest.RuntimeBackendBrowser,
55
+ }
56
+ for idx := 0; idx < len(args); idx++ {
57
+ name, value, hasValue := splitFlag(args[idx])
58
+ switch name {
59
+ case "-test.v":
60
+ if !hasValue {
61
+ req.Verbose = true
62
+ continue
63
+ }
64
+ verbose, err := parseTestBool(value)
65
+ if err != nil {
66
+ return nil, errors.Wrapf(err, "parse %s", name)
67
+ }
68
+ req.Verbose = verbose
69
+ case "-test.run":
70
+ if !hasValue {
71
+ idx++
72
+ if idx >= len(args) {
73
+ return nil, errors.New("-test.run requires a value")
74
+ }
75
+ value = args[idx]
76
+ }
77
+ req.Run = value
78
+ case "-test.count":
79
+ if !hasValue {
80
+ idx++
81
+ if idx >= len(args) {
82
+ return nil, errors.New("-test.count requires a value")
83
+ }
84
+ value = args[idx]
85
+ }
86
+ count, err := strconv.Atoi(value)
87
+ if err != nil {
88
+ return nil, errors.Wrap(err, "parse -test.count")
89
+ }
90
+ req.Count = count
91
+ case "-test.short":
92
+ if !hasValue {
93
+ req.Short = true
94
+ continue
95
+ }
96
+ short, err := parseTestBool(value)
97
+ if err != nil {
98
+ return nil, errors.Wrap(err, "parse -test.short")
99
+ }
100
+ req.Short = short
101
+ case "-test.timeout":
102
+ if !hasValue {
103
+ idx++
104
+ if idx >= len(args) {
105
+ return nil, errors.New("-test.timeout requires a value")
106
+ }
107
+ value = args[idx]
108
+ }
109
+ timeout, err := time.ParseDuration(value)
110
+ if err != nil {
111
+ return nil, errors.Wrap(err, "parse -test.timeout")
112
+ }
113
+ req.Timeout = timeout
114
+ case "-test.paniconexit0":
115
+ if !hasValue {
116
+ req.PanicOnExit0 = true
117
+ continue
118
+ }
119
+ panicOnExit0, err := parseTestBool(value)
120
+ if err != nil {
121
+ return nil, errors.Wrap(err, "parse -test.paniconexit0")
122
+ }
123
+ req.PanicOnExit0 = panicOnExit0
124
+ case "-test.parallel":
125
+ if !hasValue {
126
+ idx++
127
+ if idx >= len(args) {
128
+ return nil, errors.New("-test.parallel requires a value")
129
+ }
130
+ }
131
+ case "-test.testlogfile":
132
+ if !hasValue {
133
+ idx++
134
+ if idx >= len(args) {
135
+ return nil, errors.New("-test.testlogfile requires a value")
136
+ }
137
+ }
138
+ default:
139
+ return nil, errors.Errorf("unsupported go_js_wasm_exec flag %s", name)
140
+ }
141
+ }
142
+ return req, nil
143
+ }
144
+
145
+ func splitFlag(arg string) (string, string, bool) {
146
+ name, value, ok := strings.Cut(arg, "=")
147
+ return name, value, ok
148
+ }
149
+
150
+ func parseTestBool(value string) (bool, error) {
151
+ switch strings.ToLower(strings.TrimSpace(value)) {
152
+ case "", "1", "t", "true", "test2json":
153
+ return true, nil
154
+ case "0", "f", "false":
155
+ return false, nil
156
+ default:
157
+ return false, errors.Errorf("invalid boolean value %q", value)
158
+ }
159
+ }
160
+
161
+ func printResult(w io.Writer, result *gotest.Result) error {
162
+ if result == nil {
163
+ return errors.New("goscript browser test result is nil")
164
+ }
165
+ for _, pkg := range result.Packages {
166
+ if strings.TrimSpace(pkg.Output) != "" {
167
+ if _, err := io.WriteString(w, strings.TrimSpace(pkg.Output)+"\n"); err != nil {
168
+ return err
169
+ }
170
+ }
171
+ switch pkg.Action {
172
+ case gotest.ActionPass:
173
+ if _, err := io.WriteString(w, "ok \t"+pkg.PackagePath+"\n"); err != nil {
174
+ return err
175
+ }
176
+ case gotest.ActionSkip:
177
+ if _, err := io.WriteString(w, "? \t"+pkg.PackagePath+"\t[no test files]\n"); err != nil {
178
+ return err
179
+ }
180
+ case gotest.ActionFail:
181
+ line := "FAIL\t" + pkg.PackagePath
182
+ if pkg.Owner != "" {
183
+ line += "\towner=" + string(pkg.Owner)
184
+ }
185
+ if _, err := io.WriteString(w, line+"\n"); err != nil {
186
+ return err
187
+ }
188
+ if strings.TrimSpace(pkg.Error) != "" {
189
+ if _, err := io.WriteString(w, strings.TrimSpace(pkg.Error)+"\n"); err != nil {
190
+ return err
191
+ }
192
+ }
193
+ }
194
+ }
195
+ if result.WorkDir != "" {
196
+ if _, err := io.WriteString(w, "goscript browser test workdir: "+result.WorkDir+"\n"); err != nil {
197
+ return err
198
+ }
199
+ }
200
+ return nil
201
+ }
@@ -0,0 +1,83 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "strings"
6
+ "testing"
7
+ "time"
8
+
9
+ "github.com/aperturerobotics/goscript/compiler/gotest"
10
+ )
11
+
12
+ func TestRequestFromGoToolArgsMapsSupportedFlags(t *testing.T) {
13
+ dir := t.TempDir()
14
+ t.Chdir(dir)
15
+
16
+ req, err := requestFromGoToolArgs([]string{
17
+ "/tmp/pkg.test.wasm",
18
+ "-test.v=test2json",
19
+ "-test.run", "TestBrowser",
20
+ "-test.count=2",
21
+ "-test.short",
22
+ "-test.timeout=3s",
23
+ "-test.paniconexit0",
24
+ "-test.parallel", "8",
25
+ "-test.testlogfile=ignored",
26
+ })
27
+ if err != nil {
28
+ t.Fatalf("parse go tool args: %v", err)
29
+ }
30
+ if req.Dir != dir {
31
+ t.Fatalf("Dir = %q, want %q", req.Dir, dir)
32
+ }
33
+ if len(req.Patterns) != 1 || req.Patterns[0] != "." {
34
+ t.Fatalf("Patterns = %#v, want .", req.Patterns)
35
+ }
36
+ if req.RuntimeBackend != gotest.RuntimeBackendBrowser {
37
+ t.Fatalf("RuntimeBackend = %q, want browser", req.RuntimeBackend)
38
+ }
39
+ if !req.Verbose || req.Run != "TestBrowser" || req.Count != 2 || !req.Short || req.Timeout != 3*time.Second || !req.PanicOnExit0 {
40
+ t.Fatalf("unexpected mapped request: %#v", req)
41
+ }
42
+ }
43
+
44
+ func TestRequestFromGoToolArgsMapsPanicOnExitZeroFalse(t *testing.T) {
45
+ t.Chdir(t.TempDir())
46
+
47
+ req, err := requestFromGoToolArgs([]string{"/tmp/pkg.test.wasm", "-test.paniconexit0=false"})
48
+ if err != nil {
49
+ t.Fatalf("parse go tool args: %v", err)
50
+ }
51
+ if req.PanicOnExit0 {
52
+ t.Fatal("expected explicit -test.paniconexit0=false to disable panic-on-exit-zero")
53
+ }
54
+ }
55
+
56
+ func TestRequestFromGoToolArgsRejectsUnsupportedFlags(t *testing.T) {
57
+ t.Chdir(t.TempDir())
58
+
59
+ if _, err := requestFromGoToolArgs([]string{"/tmp/pkg.test.wasm", "-test.coverprofile=cover.out"}); err == nil {
60
+ t.Fatal("expected unsupported flag to fail")
61
+ } else if !strings.Contains(err.Error(), "unsupported go_js_wasm_exec flag -test.coverprofile") {
62
+ t.Fatalf("unexpected error: %v", err)
63
+ }
64
+ }
65
+
66
+ func TestPrintResult(t *testing.T) {
67
+ var out bytes.Buffer
68
+ err := printResult(&out, &gotest.Result{
69
+ WorkDir: "/work",
70
+ Packages: []gotest.PackageResult{{
71
+ PackagePath: "example.test/browser",
72
+ Action: gotest.ActionPass,
73
+ Output: "browser ok",
74
+ }},
75
+ })
76
+ if err != nil {
77
+ t.Fatalf("print result: %v", err)
78
+ }
79
+ got := out.String()
80
+ if !strings.Contains(got, "browser ok") || !strings.Contains(got, "ok \texample.test/browser") {
81
+ t.Fatalf("unexpected output:\n%s", got)
82
+ }
83
+ }
@@ -17,7 +17,7 @@ func compileCommands() []*cli.Command {
17
17
  func newCompileCommand() *cli.Command {
18
18
  var config compiler.Config
19
19
  var packages cli.StringSlice
20
- var buildFlags cli.StringSlice
20
+ var buildFlags rawStringSlice
21
21
  var overrideDirs cli.StringSlice
22
22
 
23
23
  return &cli.Command{
@@ -25,7 +25,7 @@ func newCompileCommand() *cli.Command {
25
25
  Category: "compile",
26
26
  Usage: "compile a Go package to TypeScript",
27
27
  Action: func(c *cli.Context) error {
28
- config.BuildFlags = slices.Clone(buildFlags.Value())
28
+ config.BuildFlags = buildFlags.Value()
29
29
  config.OverrideDirs = slices.Clone(overrideDirs.Value())
30
30
  return compilePackage(c.Context, &config, packages.Value())
31
31
  },
@@ -51,12 +51,12 @@ func newCompileCommand() *cli.Command {
51
51
  Value: "",
52
52
  EnvVars: []string{"GOSCRIPT_DIR"},
53
53
  },
54
- &cli.StringSliceFlag{
55
- Name: "build-flags",
56
- Aliases: []string{"b", "buildflags", "build-flag", "buildflag"},
57
- Usage: "Go build flags (tags) to use during analysis",
58
- Destination: &buildFlags,
59
- EnvVars: []string{"GOSCRIPT_BUILD_FLAGS"},
54
+ &cli.GenericFlag{
55
+ Name: "build-flags",
56
+ Aliases: []string{"b", "buildflags", "build-flag", "buildflag"},
57
+ Usage: "Go build flags (tags) to use during analysis",
58
+ Value: &buildFlags,
59
+ EnvVars: []string{"GOSCRIPT_BUILD_FLAGS"},
60
60
  },
61
61
  &cli.StringSliceFlag{
62
62
  Name: "gs-path",
@@ -80,10 +80,37 @@ func newCompileCommand() *cli.Command {
80
80
  Value: false,
81
81
  EnvVars: []string{"GOSCRIPT_ALL_DEPENDENCIES"},
82
82
  },
83
+ &cli.BoolFlag{
84
+ Name: "protobuf-ts-binding",
85
+ Usage: "bind .pb.go files to sibling .pb.ts files instead of emitting .pb.gs.ts",
86
+ Destination: &config.ProtobufTypeScriptBinding,
87
+ Value: false,
88
+ EnvVars: []string{"GOSCRIPT_PROTOBUF_TS_BINDING"},
89
+ },
83
90
  },
84
91
  }
85
92
  }
86
93
 
94
+ type rawStringSlice []string
95
+
96
+ func (s *rawStringSlice) Set(value string) error {
97
+ if value == "" {
98
+ return nil
99
+ }
100
+ *s = append(*s, value)
101
+ return nil
102
+ }
103
+
104
+ func (s *rawStringSlice) String() string {
105
+ // Keep the flag default empty. The CLI package registers aliases against
106
+ // the same flag value, and echoing accumulated values here re-parses them.
107
+ return ""
108
+ }
109
+
110
+ func (s *rawStringSlice) Value() []string {
111
+ return slices.Clone(*s)
112
+ }
113
+
87
114
  // compilePackage tries to compile the package.
88
115
  func compilePackage(ctx context.Context, config *compiler.Config, pkgs []string) error {
89
116
  if len(pkgs) == 0 {
@@ -32,6 +32,7 @@ func newTestCommand() *cli.Command {
32
32
  var dir string
33
33
  var parallelism int
34
34
  var runtimeGroups bool
35
+ var browser bool
35
36
  var cpuProfile string
36
37
  var memProfile string
37
38
  var incrementalTypeCheck bool
@@ -54,6 +55,7 @@ func newTestCommand() *cli.Command {
54
55
  WorkDir: workDir,
55
56
  OutputRoot: outputRoot,
56
57
  Parallelism: parallelism,
58
+ RuntimeBackend: testRuntimeBackend(browser),
57
59
  RuntimeGroups: runtimeGroups,
58
60
  IncrementalTypeCheck: incrementalTypeCheck,
59
61
  }
@@ -147,6 +149,11 @@ func newTestCommand() *cli.Command {
147
149
  Usage: "run package runtimes in grouped Bun worker processes",
148
150
  Destination: &runtimeGroups,
149
151
  },
152
+ &cli.BoolFlag{
153
+ Name: "browser",
154
+ Usage: "run package runtimes in a Chromium browser",
155
+ Destination: &browser,
156
+ },
150
157
  &cli.BoolFlag{
151
158
  Name: "incremental-typecheck",
152
159
  Usage: "reuse TypeScript build-info files in the test workdir",
@@ -166,6 +173,13 @@ func newTestCommand() *cli.Command {
166
173
  }
167
174
  }
168
175
 
176
+ func testRuntimeBackend(browser bool) gotest.RuntimeBackend {
177
+ if browser {
178
+ return gotest.RuntimeBackendBrowser
179
+ }
180
+ return gotest.RuntimeBackendBun
181
+ }
182
+
169
183
  func startCPUProfile(path string) (func(), error) {
170
184
  path = strings.TrimSpace(path)
171
185
  if path == "" {
@@ -17,7 +17,7 @@ func TestTestCommandHelp(t *testing.T) {
17
17
  t.Fatalf("test help failed: %v", err)
18
18
  }
19
19
  help := out.String()
20
- for _, expected := range []string{"compile and run Go package tests through GoScript", "--tags", "--run", "--count", "--short", "--timeout", "-p", "--runtime-groups", "--cpuprofile", "--memprofile"} {
20
+ for _, expected := range []string{"compile and run Go package tests through GoScript", "--tags", "--run", "--count", "--short", "--timeout", "-p", "--runtime-groups", "--browser", "--cpuprofile", "--memprofile"} {
21
21
  if !strings.Contains(help, expected) {
22
22
  t.Fatalf("help output missing %q:\n%s", expected, help)
23
23
  }
@@ -14,22 +14,20 @@ func TestCompileCommandForwardsBuildFlags(t *testing.T) {
14
14
  writeFile(t, filepath.Join(dir, "default.go"), strings.Join([]string{
15
15
  "//go:build !customtag",
16
16
  "",
17
- "package main",
17
+ "package cli",
18
18
  `const selected = "default"`,
19
19
  "",
20
20
  }, "\n"))
21
21
  writeFile(t, filepath.Join(dir, "tagged.go"), strings.Join([]string{
22
22
  "//go:build customtag",
23
23
  "",
24
- "package main",
24
+ "package cli",
25
25
  `const selected = "custom"`,
26
26
  "",
27
27
  }, "\n"))
28
28
  writeFile(t, filepath.Join(dir, "main.go"), strings.Join([]string{
29
- "package main",
30
- "func main() {",
31
- " println(selected)",
32
- "}",
29
+ "package cli",
30
+ "func Selected() string { return selected }",
33
31
  "",
34
32
  }, "\n"))
35
33
 
@@ -71,6 +69,107 @@ func TestCompileCommandForwardsBuildFlags(t *testing.T) {
71
69
  }
72
70
  }
73
71
 
72
+ func TestCompileCommandPreservesCommaSeparatedBuildFlagValues(t *testing.T) {
73
+ dir := t.TempDir()
74
+ outputDir := filepath.Join(dir, "output")
75
+ writeFile(t, filepath.Join(dir, "go.mod"), "module example.test/cli\n\ngo 1.25.3\n")
76
+ writeFile(t, filepath.Join(dir, "default.go"), strings.Join([]string{
77
+ "//go:build !(customtag && othertag)",
78
+ "",
79
+ "package cli",
80
+ `const selected = "default"`,
81
+ "",
82
+ }, "\n"))
83
+ writeFile(t, filepath.Join(dir, "tagged.go"), strings.Join([]string{
84
+ "//go:build customtag && othertag",
85
+ "",
86
+ "package cli",
87
+ `const selected = "custom"`,
88
+ "",
89
+ }, "\n"))
90
+ writeFile(t, filepath.Join(dir, "main.go"), strings.Join([]string{
91
+ "package cli",
92
+ "func Selected() string { return selected }",
93
+ "",
94
+ }, "\n"))
95
+
96
+ app := newApp()
97
+ err := app.Run([]string{
98
+ "goscript",
99
+ "compile",
100
+ "--package",
101
+ ".",
102
+ "--output",
103
+ outputDir,
104
+ "--dir",
105
+ dir,
106
+ "--build-flags=-tags=customtag,othertag",
107
+ })
108
+ if err != nil {
109
+ t.Fatalf("compile command failed: %v", err)
110
+ }
111
+
112
+ taggedPath := filepath.Join(outputDir, "@goscript", "example.test", "cli", "tagged.gs.ts")
113
+ tagged, err := os.ReadFile(taggedPath)
114
+ if err != nil {
115
+ t.Fatalf("read tagged generated file: %v", err)
116
+ }
117
+ if !strings.Contains(string(tagged), `"custom"`) {
118
+ t.Fatalf("expected tagged generated file to use tagged source, got:\n%s", tagged)
119
+ }
120
+ }
121
+
122
+ func TestCompileCommandPreservesCommaSeparatedBuildFlagEnvValue(t *testing.T) {
123
+ dir := t.TempDir()
124
+ outputDir := filepath.Join(dir, "output")
125
+ writeFile(t, filepath.Join(dir, "go.mod"), "module example.test/cli\n\ngo 1.25.3\n")
126
+ writeFile(t, filepath.Join(dir, "default.go"), strings.Join([]string{
127
+ "//go:build !(customtag && othertag)",
128
+ "",
129
+ "package cli",
130
+ `const selected = "default"`,
131
+ "",
132
+ }, "\n"))
133
+ writeFile(t, filepath.Join(dir, "tagged.go"), strings.Join([]string{
134
+ "//go:build customtag && othertag",
135
+ "",
136
+ "package cli",
137
+ `const selected = "custom"`,
138
+ "",
139
+ }, "\n"))
140
+ writeFile(t, filepath.Join(dir, "main.go"), strings.Join([]string{
141
+ "package cli",
142
+ "func Selected() string { return selected }",
143
+ "",
144
+ }, "\n"))
145
+
146
+ t.Setenv("GOSCRIPT_BUILD_FLAGS", "-tags=customtag,othertag")
147
+
148
+ app := newApp()
149
+ err := app.Run([]string{
150
+ "goscript",
151
+ "compile",
152
+ "--package",
153
+ ".",
154
+ "--output",
155
+ outputDir,
156
+ "--dir",
157
+ dir,
158
+ })
159
+ if err != nil {
160
+ t.Fatalf("compile command failed: %v", err)
161
+ }
162
+
163
+ taggedPath := filepath.Join(outputDir, "@goscript", "example.test", "cli", "tagged.gs.ts")
164
+ tagged, err := os.ReadFile(taggedPath)
165
+ if err != nil {
166
+ t.Fatalf("read tagged generated file: %v", err)
167
+ }
168
+ if !strings.Contains(string(tagged), `"custom"`) {
169
+ t.Fatalf("expected tagged generated file to use tagged source, got:\n%s", tagged)
170
+ }
171
+ }
172
+
74
173
  func writeFile(t *testing.T, path string, content string) {
75
174
  t.Helper()
76
175
  if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
@@ -1,8 +1,9 @@
1
1
  package compiler
2
2
 
3
- import "slices"
4
-
5
- import "strings"
3
+ import (
4
+ "slices"
5
+ "strings"
6
+ )
6
7
 
7
8
  const goScriptBuildTag = "goscript"
8
9
 
@@ -26,13 +27,11 @@ func appendBuildTag(value string, tag string) string {
26
27
  return r == ',' || r == ' ' || r == '\t' || r == '\n'
27
28
  })
28
29
  if slices.Contains(tags, tag) {
29
- return value
30
- }
31
- if strings.TrimSpace(value) == "" {
32
- return tag
30
+ return strings.Join(tags, " ")
33
31
  }
34
- if strings.ContainsAny(value, " \t\n") {
35
- return value + " " + tag
32
+ tags = append(tags, tag)
33
+ if len(tags) == 0 {
34
+ return ""
36
35
  }
37
- return value + "," + tag
36
+ return strings.Join(tags, " ")
38
37
  }
@@ -42,6 +42,8 @@ type CompileRequest struct {
42
42
  DependencyMode DependencyMode
43
43
  // RuntimeEmissionMode controls runtime package emission policy.
44
44
  RuntimeEmissionMode RuntimeEmissionMode
45
+ // ProtobufTypeScriptBinding binds .pb.go files to sibling .pb.ts files.
46
+ ProtobufTypeScriptBinding bool
45
47
  // Tests controls whether package loading includes Go package-test variants.
46
48
  Tests bool
47
49
  // AllDependencies controls whether the package graph should include deps.
@@ -75,15 +77,16 @@ func (o *CompileRequestOwner) NewRequest(conf Config, patterns []string) *Compil
75
77
  }
76
78
 
77
79
  return &CompileRequest{
78
- Patterns: normalizePatterns(patterns),
79
- Dir: strings.TrimSpace(dir),
80
- OutputPath: strings.TrimSpace(conf.OutputPath),
81
- BuildFlags: append([]string(nil), conf.BuildFlags...),
82
- OverrideDirs: append([]string(nil), conf.OverrideDirs...),
83
- DependencyMode: dependencyMode,
84
- RuntimeEmissionMode: runtimeEmissionMode,
85
- AllDependencies: conf.AllDependencies,
86
- DisableEmitBuiltin: conf.DisableEmitBuiltin,
80
+ Patterns: normalizePatterns(patterns),
81
+ Dir: strings.TrimSpace(dir),
82
+ OutputPath: strings.TrimSpace(conf.OutputPath),
83
+ BuildFlags: append([]string(nil), conf.BuildFlags...),
84
+ OverrideDirs: append([]string(nil), conf.OverrideDirs...),
85
+ DependencyMode: dependencyMode,
86
+ RuntimeEmissionMode: runtimeEmissionMode,
87
+ ProtobufTypeScriptBinding: conf.ProtobufTypeScriptBinding,
88
+ AllDependencies: conf.AllDependencies,
89
+ DisableEmitBuiltin: conf.DisableEmitBuiltin,
87
90
  }
88
91
  }
89
92
 
@@ -172,7 +172,6 @@ var expectedV2ComplianceGaps = map[string]bool{
172
172
  "promise_return_type": true,
173
173
  "protobuf_lite_ts": true,
174
174
  "receiver_variable": true,
175
- "reflect_implements": true,
176
175
  "reflect_numfield": true,
177
176
  "reserved_words": true,
178
177
  "star_expr_destructuring": true,
@@ -22,6 +22,8 @@ type Config struct {
22
22
  AllDependencies bool
23
23
  // DisableEmitBuiltin controls whether runtime packages are emitted.
24
24
  DisableEmitBuiltin bool
25
+ // ProtobufTypeScriptBinding binds .pb.go files to sibling .pb.ts files.
26
+ ProtobufTypeScriptBinding bool
25
27
  }
26
28
 
27
29
  // Validate checks the config and initializes owned defaults.