goscript 0.0.84 → 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 (428) hide show
  1. package/README.md +267 -243
  2. package/cmd/goscript/cmd-test.go +193 -0
  3. package/cmd/goscript/cmd-test_test.go +76 -0
  4. package/cmd/goscript/cmd_compile.go +70 -69
  5. package/cmd/goscript/cmd_compile_test.go +79 -0
  6. package/cmd/goscript/main.go +11 -5
  7. package/compiler/build-flags.go +38 -0
  8. package/compiler/compile-request.go +220 -0
  9. package/compiler/compiler.go +16 -1336
  10. package/compiler/compliance_test.go +188 -0
  11. package/compiler/config.go +6 -13
  12. package/compiler/diagnostic.go +70 -0
  13. package/compiler/gotest/owner.go +24 -0
  14. package/compiler/gotest/package-result.go +67 -0
  15. package/compiler/gotest/request.go +145 -0
  16. package/compiler/gotest/result.go +28 -0
  17. package/compiler/gotest/runner.go +588 -0
  18. package/compiler/gotest/runner_test.go +627 -0
  19. package/compiler/gotest/test.go +9 -0
  20. package/compiler/index.test.ts +28 -28
  21. package/compiler/index.ts +40 -72
  22. package/compiler/lowered-program.go +184 -0
  23. package/compiler/lowering.go +8072 -0
  24. package/compiler/override-facts.go +307 -0
  25. package/compiler/override-registry.go +283 -0
  26. package/compiler/override-registry_test.go +254 -0
  27. package/compiler/package-graph.go +254 -0
  28. package/compiler/package-graph_test.go +316 -0
  29. package/compiler/package-test-function.go +9 -0
  30. package/compiler/package-test-graph-package.go +40 -0
  31. package/compiler/package-test-graph-variant.go +105 -0
  32. package/compiler/package-test-graph.go +117 -0
  33. package/compiler/package-test-graph_test.go +144 -0
  34. package/compiler/result.go +13 -0
  35. package/compiler/runtime-contract.go +439 -0
  36. package/compiler/runtime-contract_test.go +104 -0
  37. package/compiler/semantic-model-types.go +113 -0
  38. package/compiler/semantic-model.go +1422 -0
  39. package/compiler/semantic-model_test.go +471 -0
  40. package/compiler/service.go +133 -0
  41. package/compiler/skeleton_test.go +1775 -0
  42. package/compiler/tsworkspace/owner.go +334 -0
  43. package/compiler/tsworkspace/owner_test.go +93 -0
  44. package/compiler/tsworkspace/result.go +17 -0
  45. package/compiler/typescript-emitter.go +1040 -0
  46. package/compiler/wasm/compile.go +2 -3
  47. package/compiler/wasm/compile_test.go +79 -0
  48. package/compiler/wasm_api.go +140 -124
  49. package/dist/compiler/index.d.ts +1 -3
  50. package/dist/compiler/index.js +31 -55
  51. package/dist/compiler/index.js.map +1 -1
  52. package/dist/gs/builtin/builtin.d.ts +33 -2
  53. package/dist/gs/builtin/builtin.js +217 -6
  54. package/dist/gs/builtin/builtin.js.map +1 -1
  55. package/dist/gs/builtin/channel.d.ts +11 -3
  56. package/dist/gs/builtin/channel.js +12 -0
  57. package/dist/gs/builtin/channel.js.map +1 -1
  58. package/dist/gs/builtin/hostio.d.ts +15 -1
  59. package/dist/gs/builtin/hostio.js +134 -49
  60. package/dist/gs/builtin/hostio.js.map +1 -1
  61. package/dist/gs/builtin/index.d.ts +1 -0
  62. package/dist/gs/builtin/index.js +1 -0
  63. package/dist/gs/builtin/index.js.map +1 -1
  64. package/dist/gs/builtin/slice.d.ts +23 -3
  65. package/dist/gs/builtin/slice.js +216 -44
  66. package/dist/gs/builtin/slice.js.map +1 -1
  67. package/dist/gs/builtin/type.d.ts +16 -2
  68. package/dist/gs/builtin/type.js +134 -21
  69. package/dist/gs/builtin/type.js.map +1 -1
  70. package/dist/gs/builtin/varRef.d.ts +5 -0
  71. package/dist/gs/builtin/varRef.js +23 -0
  72. package/dist/gs/builtin/varRef.js.map +1 -1
  73. package/dist/gs/bytes/buffer.gs.js +48 -44
  74. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  75. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  76. package/dist/gs/bytes/reader.gs.js +20 -18
  77. package/dist/gs/bytes/reader.gs.js.map +1 -1
  78. package/dist/gs/context/context.d.ts +5 -4
  79. package/dist/gs/context/context.js +10 -10
  80. package/dist/gs/context/context.js.map +1 -1
  81. package/dist/gs/crypto/internal/fips140deps/byteorder/index.d.ts +1 -0
  82. package/dist/gs/crypto/internal/fips140deps/byteorder/index.js +2 -0
  83. package/dist/gs/crypto/internal/fips140deps/byteorder/index.js.map +1 -0
  84. package/dist/gs/crypto/internal/fips140deps/godebug/index.d.ts +1 -0
  85. package/dist/gs/crypto/internal/fips140deps/godebug/index.js +2 -0
  86. package/dist/gs/crypto/internal/fips140deps/godebug/index.js.map +1 -0
  87. package/dist/gs/crypto/rand/index.d.ts +5 -0
  88. package/dist/gs/crypto/rand/index.js +77 -0
  89. package/dist/gs/crypto/rand/index.js.map +1 -0
  90. package/dist/gs/embed/index.d.ts +7 -0
  91. package/dist/gs/embed/index.js +16 -0
  92. package/dist/gs/embed/index.js.map +1 -0
  93. package/dist/gs/encoding/json/index.d.ts +4 -0
  94. package/dist/gs/encoding/json/index.js +178 -0
  95. package/dist/gs/encoding/json/index.js.map +1 -0
  96. package/dist/gs/errors/errors.d.ts +4 -0
  97. package/dist/gs/errors/errors.js +81 -0
  98. package/dist/gs/errors/errors.js.map +1 -1
  99. package/dist/gs/fmt/fmt.d.ts +4 -4
  100. package/dist/gs/fmt/fmt.js +42 -11
  101. package/dist/gs/fmt/fmt.js.map +1 -1
  102. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +36 -1
  103. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +212 -2
  104. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  105. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +189 -0
  106. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +825 -0
  107. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -0
  108. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +163 -0
  109. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +449 -0
  110. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -0
  111. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  112. package/dist/gs/github.com/klauspost/compress/internal/le/index.d.ts +9 -0
  113. package/dist/gs/github.com/klauspost/compress/internal/le/index.js +71 -0
  114. package/dist/gs/github.com/klauspost/compress/internal/le/index.js.map +1 -0
  115. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  116. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  117. package/dist/gs/go/internal/scannerhooks/index.d.ts +3 -0
  118. package/dist/gs/go/internal/scannerhooks/index.js +5 -0
  119. package/dist/gs/go/internal/scannerhooks/index.js.map +1 -0
  120. package/dist/gs/go/scanner/index.d.ts +42 -0
  121. package/dist/gs/go/scanner/index.js +155 -0
  122. package/dist/gs/go/scanner/index.js.map +1 -0
  123. package/dist/gs/go/token/index.d.ts +187 -0
  124. package/dist/gs/go/token/index.js +578 -0
  125. package/dist/gs/go/token/index.js.map +1 -0
  126. package/dist/gs/internal/abi/index.d.ts +4 -0
  127. package/dist/gs/internal/abi/index.js +10 -0
  128. package/dist/gs/internal/abi/index.js.map +1 -1
  129. package/dist/gs/internal/bytealg/index.d.ts +2 -0
  130. package/dist/gs/internal/bytealg/index.js +14 -0
  131. package/dist/gs/internal/bytealg/index.js.map +1 -1
  132. package/dist/gs/internal/byteorder/index.d.ts +8 -2
  133. package/dist/gs/internal/byteorder/index.js +56 -25
  134. package/dist/gs/internal/byteorder/index.js.map +1 -1
  135. package/dist/gs/internal/godebug/index.d.ts +12 -0
  136. package/dist/gs/internal/godebug/index.js +30 -0
  137. package/dist/gs/internal/godebug/index.js.map +1 -0
  138. package/dist/gs/io/fs/fs.js.map +1 -1
  139. package/dist/gs/io/fs/index.d.ts +1 -0
  140. package/dist/gs/io/fs/index.js +1 -0
  141. package/dist/gs/io/fs/index.js.map +1 -1
  142. package/dist/gs/io/fs/readdir.js.map +1 -1
  143. package/dist/gs/io/fs/readfile.js.map +1 -1
  144. package/dist/gs/io/fs/readlink.d.ts +8 -0
  145. package/dist/gs/io/fs/readlink.js +64 -0
  146. package/dist/gs/io/fs/readlink.js.map +1 -0
  147. package/dist/gs/io/fs/stat.js.map +1 -1
  148. package/dist/gs/io/fs/sub.js.map +1 -1
  149. package/dist/gs/io/fs/walk.d.ts +3 -3
  150. package/dist/gs/io/fs/walk.js +7 -7
  151. package/dist/gs/io/fs/walk.js.map +1 -1
  152. package/dist/gs/io/io.d.ts +40 -6
  153. package/dist/gs/io/io.js +151 -26
  154. package/dist/gs/io/io.js.map +1 -1
  155. package/dist/gs/maps/iter.d.ts +3 -3
  156. package/dist/gs/maps/iter.js +3 -3
  157. package/dist/gs/maps/iter.js.map +1 -1
  158. package/dist/gs/maps/maps.d.ts +2 -2
  159. package/dist/gs/maps/maps.js +1 -1
  160. package/dist/gs/maps/maps.js.map +1 -1
  161. package/dist/gs/math/bits/index.d.ts +13 -4
  162. package/dist/gs/math/bits/index.js +66 -34
  163. package/dist/gs/math/bits/index.js.map +1 -1
  164. package/dist/gs/math/const.gs.d.ts +5 -5
  165. package/dist/gs/math/const.gs.js +4 -4
  166. package/dist/gs/math/const.gs.js.map +1 -1
  167. package/dist/gs/mime/index.d.ts +1 -0
  168. package/dist/gs/mime/index.js +50 -0
  169. package/dist/gs/mime/index.js.map +1 -0
  170. package/dist/gs/net/http/httptest/index.d.ts +11 -0
  171. package/dist/gs/net/http/httptest/index.js +21 -0
  172. package/dist/gs/net/http/httptest/index.js.map +1 -0
  173. package/dist/gs/net/http/index.d.ts +27 -0
  174. package/dist/gs/net/http/index.js +61 -0
  175. package/dist/gs/net/http/index.js.map +1 -0
  176. package/dist/gs/os/dir_unix.gs.js +2 -2
  177. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  178. package/dist/gs/os/error.gs.js +2 -4
  179. package/dist/gs/os/error.gs.js.map +1 -1
  180. package/dist/gs/os/exec.gs.js.map +1 -1
  181. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  182. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  183. package/dist/gs/os/root_js.gs.js.map +1 -1
  184. package/dist/gs/os/tempfile.gs.js +66 -9
  185. package/dist/gs/os/tempfile.gs.js.map +1 -1
  186. package/dist/gs/os/types.gs.js.map +1 -1
  187. package/dist/gs/os/types_js.gs.js +9 -9
  188. package/dist/gs/os/types_js.gs.js.map +1 -1
  189. package/dist/gs/os/types_unix.gs.js.map +1 -1
  190. package/dist/gs/path/filepath/match.js +165 -3
  191. package/dist/gs/path/filepath/match.js.map +1 -1
  192. package/dist/gs/path/filepath/path.d.ts +3 -1
  193. package/dist/gs/path/filepath/path.js +133 -4
  194. package/dist/gs/path/filepath/path.js.map +1 -1
  195. package/dist/gs/path/match.js.map +1 -1
  196. package/dist/gs/path/path.d.ts +4 -1
  197. package/dist/gs/path/path.js +16 -4
  198. package/dist/gs/path/path.js.map +1 -1
  199. package/dist/gs/reflect/index.d.ts +3 -3
  200. package/dist/gs/reflect/index.js +2 -2
  201. package/dist/gs/reflect/index.js.map +1 -1
  202. package/dist/gs/reflect/map.js +3 -0
  203. package/dist/gs/reflect/map.js.map +1 -1
  204. package/dist/gs/reflect/type.d.ts +9 -5
  205. package/dist/gs/reflect/type.js +233 -21
  206. package/dist/gs/reflect/type.js.map +1 -1
  207. package/dist/gs/reflect/types.js.map +1 -1
  208. package/dist/gs/reflect/visiblefields.js.map +1 -1
  209. package/dist/gs/runtime/debug/index.d.ts +2 -0
  210. package/dist/gs/runtime/debug/index.js +8 -0
  211. package/dist/gs/runtime/debug/index.js.map +1 -0
  212. package/dist/gs/runtime/runtime.d.ts +35 -3
  213. package/dist/gs/runtime/runtime.js +72 -0
  214. package/dist/gs/runtime/runtime.js.map +1 -1
  215. package/dist/gs/slices/slices.d.ts +24 -5
  216. package/dist/gs/slices/slices.js +214 -5
  217. package/dist/gs/slices/slices.js.map +1 -1
  218. package/dist/gs/sort/slice.gs.d.ts +3 -3
  219. package/dist/gs/sort/slice.gs.js +6 -6
  220. package/dist/gs/sort/slice.gs.js.map +1 -1
  221. package/dist/gs/sort/sort.gs.d.ts +4 -4
  222. package/dist/gs/sort/sort.gs.js +11 -8
  223. package/dist/gs/sort/sort.gs.js.map +1 -1
  224. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  225. package/dist/gs/strconv/quote.gs.js.map +1 -1
  226. package/dist/gs/strings/builder.d.ts +1 -1
  227. package/dist/gs/strings/builder.js +3 -2
  228. package/dist/gs/strings/builder.js.map +1 -1
  229. package/dist/gs/strings/reader.js.map +1 -1
  230. package/dist/gs/strings/replace.js.map +1 -1
  231. package/dist/gs/sync/atomic/type.gs.d.ts +9 -8
  232. package/dist/gs/sync/atomic/type.gs.js +0 -2
  233. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  234. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  235. package/dist/gs/sync/sync.d.ts +3 -0
  236. package/dist/gs/sync/sync.js +39 -0
  237. package/dist/gs/sync/sync.js.map +1 -1
  238. package/dist/gs/syscall/constants.d.ts +36 -24
  239. package/dist/gs/syscall/constants.js +12 -0
  240. package/dist/gs/syscall/constants.js.map +1 -1
  241. package/dist/gs/syscall/errors.d.ts +2 -0
  242. package/dist/gs/syscall/errors.js +8 -0
  243. package/dist/gs/syscall/errors.js.map +1 -1
  244. package/dist/gs/syscall/fs.d.ts +43 -0
  245. package/dist/gs/syscall/fs.js +102 -0
  246. package/dist/gs/syscall/fs.js.map +1 -1
  247. package/dist/gs/syscall/js/index.d.ts +90 -0
  248. package/dist/gs/syscall/js/index.js +375 -0
  249. package/dist/gs/syscall/js/index.js.map +1 -0
  250. package/dist/gs/syscall/types.d.ts +22 -0
  251. package/dist/gs/syscall/types.js +45 -1
  252. package/dist/gs/syscall/types.js.map +1 -1
  253. package/dist/gs/testing/index.d.ts +1 -0
  254. package/dist/gs/testing/index.js +2 -0
  255. package/dist/gs/testing/index.js.map +1 -0
  256. package/dist/gs/testing/testing.d.ts +77 -0
  257. package/dist/gs/testing/testing.js +301 -0
  258. package/dist/gs/testing/testing.js.map +1 -0
  259. package/dist/gs/time/time.d.ts +41 -4
  260. package/dist/gs/time/time.js +205 -36
  261. package/dist/gs/time/time.js.map +1 -1
  262. package/dist/gs/unicode/unicode.d.ts +23 -1
  263. package/dist/gs/unicode/unicode.js +79 -10
  264. package/dist/gs/unicode/unicode.js.map +1 -1
  265. package/dist/gs/unicode/utf8/utf8.d.ts +4 -4
  266. package/dist/gs/unicode/utf8/utf8.js +24 -11
  267. package/dist/gs/unicode/utf8/utf8.js.map +1 -1
  268. package/dist/gs/unique/index.d.ts +11 -0
  269. package/dist/gs/unique/index.js +71 -0
  270. package/dist/gs/unique/index.js.map +1 -0
  271. package/go.mod +2 -2
  272. package/go.sum +9 -0
  273. package/gs/builtin/builtin.ts +266 -8
  274. package/gs/builtin/channel.ts +22 -0
  275. package/gs/builtin/hostio.test.ts +177 -0
  276. package/gs/builtin/hostio.ts +171 -56
  277. package/gs/builtin/index.ts +1 -0
  278. package/gs/builtin/runtime-contract.test.ts +356 -0
  279. package/gs/builtin/slice.ts +259 -50
  280. package/gs/builtin/type.ts +188 -30
  281. package/gs/builtin/varRef.ts +38 -1
  282. package/gs/bytes/buffer.gs.ts +48 -44
  283. package/gs/bytes/meta.json +8 -3
  284. package/gs/bytes/reader.gs.ts +20 -19
  285. package/gs/context/context.test.ts +41 -0
  286. package/gs/context/context.ts +22 -26
  287. package/gs/crypto/internal/fips140deps/byteorder/index.ts +1 -0
  288. package/gs/crypto/internal/fips140deps/godebug/index.ts +1 -0
  289. package/gs/crypto/rand/index.test.ts +32 -0
  290. package/gs/crypto/rand/index.ts +90 -0
  291. package/gs/crypto/rand/meta.json +5 -0
  292. package/gs/embed/index.ts +20 -0
  293. package/gs/embed/meta.json +5 -0
  294. package/gs/encoding/json/index.test.ts +79 -0
  295. package/gs/encoding/json/index.ts +210 -0
  296. package/gs/errors/errors.test.ts +82 -0
  297. package/gs/errors/errors.ts +104 -0
  298. package/gs/fmt/fmt.ts +56 -16
  299. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +95 -0
  300. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +300 -2
  301. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +159 -0
  302. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +1005 -0
  303. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +719 -0
  304. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +40 -0
  305. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  306. package/gs/github.com/klauspost/compress/internal/le/index.test.ts +36 -0
  307. package/gs/github.com/klauspost/compress/internal/le/index.ts +114 -0
  308. package/gs/go/internal/scannerhooks/index.test.ts +14 -0
  309. package/gs/go/internal/scannerhooks/index.ts +9 -0
  310. package/gs/go/scanner/index.test.ts +72 -0
  311. package/gs/go/scanner/index.ts +204 -0
  312. package/gs/go/token/index.test.ts +67 -0
  313. package/gs/go/token/index.ts +686 -0
  314. package/gs/internal/abi/index.test.ts +18 -0
  315. package/gs/internal/abi/index.ts +14 -0
  316. package/gs/internal/bytealg/index.test.ts +18 -0
  317. package/gs/internal/bytealg/index.ts +16 -0
  318. package/gs/internal/byteorder/index.test.ts +39 -0
  319. package/gs/internal/byteorder/index.ts +100 -27
  320. package/gs/internal/godebug/index.test.ts +16 -0
  321. package/gs/internal/godebug/index.ts +35 -0
  322. package/gs/io/fs/index.ts +1 -0
  323. package/gs/io/fs/meta.json +5 -0
  324. package/gs/io/fs/readlink.test.ts +43 -0
  325. package/gs/io/fs/readlink.ts +77 -0
  326. package/gs/io/fs/walk.test.ts +61 -0
  327. package/gs/io/fs/walk.ts +9 -9
  328. package/gs/io/io.ts +174 -31
  329. package/gs/io/meta.json +10 -2
  330. package/gs/maps/iter.ts +12 -6
  331. package/gs/maps/maps.ts +8 -6
  332. package/gs/math/bits/index.ts +103 -47
  333. package/gs/math/const.gs.test.ts +11 -5
  334. package/gs/math/const.gs.ts +5 -6
  335. package/gs/mime/index.ts +54 -0
  336. package/gs/net/http/httptest/index.ts +25 -0
  337. package/gs/net/http/index.test.ts +20 -0
  338. package/gs/net/http/index.ts +81 -0
  339. package/gs/os/dir_unix.gs.ts +2 -3
  340. package/gs/os/file_unix_js.test.ts +50 -0
  341. package/gs/os/meta.json +1 -2
  342. package/gs/os/tempfile.gs.test.ts +85 -0
  343. package/gs/os/tempfile.gs.ts +71 -11
  344. package/gs/os/types_js.gs.ts +11 -11
  345. package/gs/path/filepath/match.test.ts +31 -12
  346. package/gs/path/filepath/match.ts +178 -3
  347. package/gs/path/filepath/path.test.ts +25 -0
  348. package/gs/path/filepath/path.ts +159 -5
  349. package/gs/path/path.ts +20 -5
  350. package/gs/reflect/index.ts +2 -1
  351. package/gs/reflect/map.test.ts +19 -0
  352. package/gs/reflect/map.ts +4 -0
  353. package/gs/reflect/type.ts +298 -29
  354. package/gs/reflect/typefor.test.ts +75 -0
  355. package/gs/runtime/debug/index.test.ts +24 -0
  356. package/gs/runtime/debug/index.ts +8 -0
  357. package/gs/runtime/runtime.test.ts +19 -0
  358. package/gs/runtime/runtime.ts +98 -3
  359. package/gs/slices/slices.test.ts +94 -0
  360. package/gs/slices/slices.ts +245 -5
  361. package/gs/sort/meta.json +7 -0
  362. package/gs/sort/slice.gs.ts +16 -7
  363. package/gs/sort/sort.gs.ts +16 -13
  364. package/gs/strings/builder.ts +4 -3
  365. package/gs/sync/atomic/type.gs.ts +13 -14
  366. package/gs/sync/meta.json +3 -1
  367. package/gs/sync/sync.test.ts +36 -0
  368. package/gs/sync/sync.ts +39 -0
  369. package/gs/syscall/constants.ts +39 -24
  370. package/gs/syscall/errors.ts +10 -0
  371. package/gs/syscall/fs.ts +195 -0
  372. package/gs/syscall/js/index.ts +458 -0
  373. package/gs/syscall/js/meta.json +4 -0
  374. package/gs/syscall/net.test.ts +85 -0
  375. package/gs/syscall/types.ts +56 -0
  376. package/gs/testing/index.ts +1 -0
  377. package/gs/testing/meta.json +5 -0
  378. package/gs/testing/testing.test.ts +90 -0
  379. package/gs/testing/testing.ts +382 -0
  380. package/gs/time/time.test.ts +106 -0
  381. package/gs/time/time.ts +278 -57
  382. package/gs/unicode/unicode.test.ts +25 -0
  383. package/gs/unicode/unicode.ts +119 -9
  384. package/gs/unicode/utf8/utf8.test.ts +13 -0
  385. package/gs/unicode/utf8/utf8.ts +28 -16
  386. package/gs/unique/index.ts +91 -0
  387. package/package.json +14 -13
  388. package/compiler/analysis.go +0 -3475
  389. package/compiler/analysis_test.go +0 -338
  390. package/compiler/assignment.go +0 -580
  391. package/compiler/builtin_test.go +0 -92
  392. package/compiler/code-writer.go +0 -115
  393. package/compiler/compiler_test.go +0 -149
  394. package/compiler/composite-lit.go +0 -779
  395. package/compiler/config_test.go +0 -62
  396. package/compiler/constraint.go +0 -86
  397. package/compiler/decl.go +0 -801
  398. package/compiler/expr-call-async.go +0 -188
  399. package/compiler/expr-call-builtins.go +0 -208
  400. package/compiler/expr-call-helpers.go +0 -382
  401. package/compiler/expr-call-make.go +0 -318
  402. package/compiler/expr-call-type-conversion.go +0 -520
  403. package/compiler/expr-call.go +0 -413
  404. package/compiler/expr-selector.go +0 -343
  405. package/compiler/expr-star.go +0 -82
  406. package/compiler/expr-type.go +0 -442
  407. package/compiler/expr-value.go +0 -89
  408. package/compiler/expr.go +0 -773
  409. package/compiler/field.go +0 -183
  410. package/compiler/gs_dependencies_test.go +0 -298
  411. package/compiler/lit.go +0 -322
  412. package/compiler/output.go +0 -72
  413. package/compiler/primitive.go +0 -149
  414. package/compiler/protobuf.go +0 -697
  415. package/compiler/sanitize.go +0 -100
  416. package/compiler/spec-struct.go +0 -995
  417. package/compiler/spec-value.go +0 -540
  418. package/compiler/spec.go +0 -725
  419. package/compiler/stmt-assign.go +0 -664
  420. package/compiler/stmt-for.go +0 -266
  421. package/compiler/stmt-range.go +0 -475
  422. package/compiler/stmt-select.go +0 -262
  423. package/compiler/stmt-type-switch.go +0 -147
  424. package/compiler/stmt.go +0 -1308
  425. package/compiler/type-assert.go +0 -386
  426. package/compiler/type-info.go +0 -156
  427. package/compiler/type-utils.go +0 -207
  428. package/compiler/type.go +0 -892
@@ -2,1360 +2,40 @@ package compiler
2
2
 
3
3
  import (
4
4
  "context"
5
- "crypto/sha256"
6
- "encoding/hex"
7
- "encoding/json"
8
- "fmt"
9
- "go/ast"
10
- "go/constant"
11
- "go/token"
12
- "go/types"
13
- "os"
14
- "path/filepath"
15
- "slices"
16
- "strings"
17
5
 
18
- gs "github.com/aperturerobotics/goscript"
19
6
  "github.com/sirupsen/logrus"
20
- "golang.org/x/mod/modfile"
21
7
  "golang.org/x/tools/go/packages"
22
8
  )
23
9
 
24
- // Compiler is the root compiler for a project. It orchestrates the loading
25
- // and compilation of Go packages into TypeScript. It holds project-wide
26
- // configuration and uses `golang.org/x/tools/go/packages` to load
27
- // Go package information.
10
+ // Compiler is the public Go adapter for the v2 compile service.
28
11
  type Compiler struct {
29
- le *logrus.Entry
30
- config Config
31
- opts packages.Config
12
+ le *logrus.Entry
13
+ config Config
14
+ service *CompileService
32
15
  }
33
16
 
34
- // NewCompiler builds a new Compiler instance.
35
- // It takes a compiler configuration, a logger entry, and an optional
36
- // `packages.Config` for loading Go packages. If `opts` is nil,
37
- // default options are used, configured for JavaScript/WebAssembly (js/wasm)
38
- // target and to load comprehensive package information (types, syntax, etc.).
39
- // It validates the provided configuration before creating the compiler.
40
- func NewCompiler(conf *Config, le *logrus.Entry, opts *packages.Config) (*Compiler, error) {
17
+ // NewCompiler builds a compiler adapter over the v2 compile service.
18
+ func NewCompiler(conf *Config, le *logrus.Entry, _ *packages.Config) (*Compiler, error) {
41
19
  if err := conf.Validate(); err != nil {
42
20
  return nil, err
43
21
  }
44
22
 
45
- if opts == nil {
46
- opts = &packages.Config{Env: os.Environ()}
47
- }
48
- // opts.Logf = c.le.Debugf
49
- opts.Tests = false
50
- opts.Env = append(opts.Env, "GOOS=js", "GOARCH=wasm")
51
- opts.Dir = conf.Dir
52
- opts.BuildFlags = conf.BuildFlags
53
-
54
- // NeedName adds Name and PkgPath.
55
- // NeedFiles adds GoFiles and OtherFiles.
56
- // NeedCompiledGoFiles adds CompiledGoFiles.
57
- // NeedImports adds Imports. If NeedDeps is not set, the Imports field will contain
58
- // "placeholder" Packages with only the ID set.
59
- // NeedDeps adds the fields requested by the LoadMode in the packages in Imports.
60
- // NeedExportsFile adds ExportsFile.
61
- // NeedTypes adds Types, Fset, and IllTyped.
62
- // NeedSyntax adds Syntax.
63
- // NeedTypesInfo adds TypesInfo.
64
- // NeedTypesSizes adds TypesSizes.
65
- // TODO: disable these if not needed
66
- opts.Mode |= packages.NeedName |
67
- packages.NeedFiles |
68
- packages.NeedCompiledGoFiles |
69
- packages.NeedImports |
70
- packages.NeedDeps |
71
- packages.NeedExportFile |
72
- packages.NeedTypes |
73
- packages.NeedSyntax |
74
- packages.NeedTypesInfo |
75
- packages.NeedTypesSizes
76
-
77
23
  return &Compiler{
78
- config: *conf,
79
- le: le,
80
- opts: *opts,
24
+ le: le,
25
+ config: *conf,
26
+ service: NewCompileService(),
81
27
  }, nil
82
28
  }
83
29
 
84
- // CompilationResult contains information about what was compiled
85
- type CompilationResult struct {
86
- // CompiledPackages contains the package paths of all packages that were actually compiled to TypeScript
87
- CompiledPackages []string
88
- // CopiedPackages contains the package paths of all packages that were copied from handwritten sources
89
- CopiedPackages []string
90
- // OriginalPackages contains the package paths that were explicitly requested for compilation
91
- OriginalPackages []string
92
- }
93
-
94
- // resolveReplaceDirectives transforms local path patterns (like ./subpkg) to their
95
- // corresponding module paths using replace directives from go.mod.
96
- // This allows users to specify local paths that are mapped via replace directives.
97
- // For example, if go.mod has:
98
- //
99
- // replace github.com/example/lib => ./local/lib
100
- //
101
- // And the user runs: goscript compile ./local/lib
102
- // This function transforms ./local/lib to github.com/example/lib
103
- func (c *Compiler) resolveReplaceDirectives(patterns []string) ([]string, error) {
104
- dir := c.config.Dir
105
- if dir == "" {
106
- dir = "."
107
- }
108
- goModPath := filepath.Join(dir, "go.mod")
109
-
110
- data, err := os.ReadFile(goModPath)
111
- if err != nil {
112
- if os.IsNotExist(err) {
113
- return patterns, nil
114
- }
115
- return nil, err
116
- }
117
-
118
- modFile, err := modfile.Parse(goModPath, data, nil)
119
- if err != nil {
120
- return nil, err
121
- }
122
-
123
- // Build replace map: local path -> module path
124
- // The replace directive syntax is: replace old => new
125
- // For local replacements: replace github.com/example/lib => ./local/lib
126
- // So r.Old.Path = "github.com/example/lib" (the module path)
127
- // And r.New.Path = "./local/lib" (the local replacement path)
128
- replaceMap := make(map[string]string)
129
- for _, r := range modFile.Replace {
130
- if r.New.Version == "" { // Local path replacements have no version
131
- // Normalize the path for consistent comparison across platforms
132
- localPath := filepath.Clean(r.New.Path)
133
- replaceMap[localPath] = r.Old.Path
134
- }
135
- }
136
-
137
- // Transform patterns that are local paths to their module path equivalents
138
- result := make([]string, len(patterns))
139
- for i, pattern := range patterns {
140
- // Check if this is a filesystem path (relative or absolute)
141
- if filepath.IsAbs(pattern) || strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") {
142
- // Normalize the pattern for consistent comparison
143
- normalizedPattern := filepath.Clean(pattern)
144
- if modulePath, ok := replaceMap[normalizedPattern]; ok {
145
- result[i] = modulePath
146
- continue
147
- }
148
- }
149
- result[i] = pattern
150
- }
151
-
152
- return result, nil
153
- }
154
-
155
- // CompilePackages loads Go packages based on the provided patterns and
156
- // then compiles each loaded package into TypeScript. It uses the context for
157
- // cancellation and applies the compiler's configured options during package loading.
158
- // For each successfully loaded package, it creates a `PackageCompiler` and
159
- // invokes its `Compile` method.
160
- // If c.config.AllDependencies is true, it will also compile all dependencies
161
- // of the requested packages, including standard library dependencies.
162
- // Returns a CompilationResult with information about what was compiled.
30
+ // CompilePackages compiles Go package patterns through the v2 pipeline.
163
31
  func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*CompilationResult, error) {
164
- opts := c.opts
165
- opts.Context = ctx
166
-
167
- // Resolve local path patterns using replace directives from go.mod
168
- resolvedPatterns, err := c.resolveReplaceDirectives(patterns)
169
- if err != nil {
170
- return nil, fmt.Errorf("failed to resolve replace directives: %w", err)
171
- }
172
-
173
- // First, load the initial packages with NeedImports to get all dependencies
174
- opts.Mode |= packages.NeedImports
175
- pkgs, err := packages.Load(&opts, resolvedPatterns...)
176
- if err != nil {
177
- return nil, fmt.Errorf("failed to load packages: %w", err)
178
- }
179
-
180
- // build a list of packages that patterns matched
181
- patternPkgPaths := make([]string, 0, len(pkgs))
182
- for _, pkg := range pkgs {
183
- patternPkgPaths = append(patternPkgPaths, pkg.PkgPath)
184
- }
185
-
186
- result := &CompilationResult{
187
- OriginalPackages: patternPkgPaths,
188
- }
189
-
190
- // If AllDependencies is true, we need to collect all dependencies
191
- if c.config.AllDependencies {
192
- // Create a set to track processed packages by their ID
193
- processed := make(map[string]bool)
194
- var allPkgs []*packages.Package
195
-
196
- // Helper function to check if a package has a handwritten equivalent
197
- hasHandwrittenEquivalent := func(pkgPath string) bool {
198
- gsSourcePath := "gs/" + pkgPath
199
- _, gsErr := gs.GsOverrides.ReadDir(gsSourcePath)
200
- return gsErr == nil
201
- }
202
-
203
- // Visit all packages and their dependencies
204
- var visit func(pkg *packages.Package)
205
- visit = func(pkg *packages.Package) {
206
- if pkg == nil || processed[pkg.ID] {
207
- return
208
- }
209
- processed[pkg.ID] = true
210
-
211
- // Add this package to the list of all packages
212
- allPkgs = append(allPkgs, pkg)
213
-
214
- // Check if this package has a handwritten equivalent
215
- if hasHandwrittenEquivalent(pkg.PkgPath) {
216
- // Add this package but don't visit its dependencies
217
- return
218
- }
219
-
220
- // Visit all imports, including standard library packages
221
- for _, imp := range pkg.Imports {
222
- // Skip protobuf-go-lite packages and their dependencies
223
- if isProtobufGoLitePackage(imp.PkgPath) {
224
- continue
225
- }
226
-
227
- // Skip packages that are only used by .pb.go files
228
- if isPackageOnlyUsedByProtobufFiles(pkg, imp.PkgPath) {
229
- continue
230
- }
231
-
232
- visit(imp)
233
- }
234
- }
235
-
236
- // Start visiting from the initial packages
237
- for _, pkg := range pkgs {
238
- visit(pkg)
239
- }
240
-
241
- // Now we have collected all dependencies, but they only have minimal information.
242
- // We need to reload them with complete type information for compilation.
243
- // Collect all package paths from the dependency graph
244
- var pkgPaths []string
245
- pkgPathSet := make(map[string]bool) // Use set to avoid duplicates
246
-
247
- for _, pkg := range allPkgs {
248
- if pkg.PkgPath != "" && !pkgPathSet[pkg.PkgPath] {
249
- pkgPaths = append(pkgPaths, pkg.PkgPath)
250
- pkgPathSet[pkg.PkgPath] = true
251
- }
252
- }
253
-
254
- // Reload all collected packages with complete type information
255
- if len(pkgPaths) > 0 {
256
- fullOpts := c.opts
257
- fullOpts.Context = ctx
258
- // Use LoadAllSyntax to get complete type information, syntax trees, and type checking
259
- fullOpts.Mode = packages.LoadAllSyntax
260
-
261
- reloadedPkgs, err := packages.Load(&fullOpts, pkgPaths...)
262
- if err != nil {
263
- return nil, fmt.Errorf("failed to reload packages with complete type information: %w", err)
264
- }
265
-
266
- // Replace the minimal packages with the fully loaded ones
267
- pkgs = reloadedPkgs
268
- } else {
269
- // No packages to reload, use the original set
270
- pkgs = allPkgs
271
- }
272
- }
273
-
274
- // If DisableEmitBuiltin is false, we need to copy the builtin package to the output directory
275
- if !c.config.DisableEmitBuiltin {
276
- c.le.Debugf("Copying builtin package to output directory")
277
- builtinPath := "gs/builtin"
278
- outputPath := ComputeModulePath(c.config.OutputPath, "builtin")
279
- if err := c.copyEmbeddedPackage(builtinPath, outputPath); err != nil {
280
- return nil, fmt.Errorf("failed to copy builtin package to output directory: %w", err)
281
- }
282
- result.CopiedPackages = append(result.CopiedPackages, "builtin")
283
- }
284
-
285
- // Track which gs packages have been processed to avoid duplicates
286
- processedGsPackages := make(map[string]bool)
287
-
288
- // Create a map of all loaded packages for dependency analysis
289
- allPackages := make(map[string]*packages.Package)
290
- for _, pkg := range pkgs {
291
- allPackages[pkg.PkgPath] = pkg
292
- }
293
-
294
- // Compile all packages
295
- for _, pkg := range pkgs {
296
- // Check if the package has a handwritten equivalent
297
- // If the package was explicitly requested, skip this logic
298
- if !slices.Contains(patternPkgPaths, pkg.PkgPath) {
299
- gsSourcePath := "gs/" + pkg.PkgPath
300
- _, gsErr := gs.GsOverrides.ReadDir(gsSourcePath)
301
- if gsErr != nil && !os.IsNotExist(gsErr) {
302
- return nil, gsErr
303
- }
304
- if gsErr == nil {
305
- if c.config.DisableEmitBuiltin {
306
- // c.le.Infof("Skipping compilation for overridden package %s", pkg.PkgPath)
307
- result.CopiedPackages = append(result.CopiedPackages, pkg.PkgPath)
308
- continue
309
- } else {
310
- // If DisableEmitBuiltin is false, we need to copy the handwritten package and its dependencies
311
- if err := c.copyGsPackageWithDependencies(pkg.PkgPath, processedGsPackages, result); err != nil {
312
- return nil, fmt.Errorf("failed to copy handwritten package %s with dependencies: %w", pkg.PkgPath, err)
313
- }
314
- continue
315
- }
316
- }
317
- }
318
-
319
- // Fail fast on packages that failed to load to avoid hiding errors
320
- if len(pkg.Errors) > 0 {
321
- var msgs []string
322
- for _, e := range pkg.Errors {
323
- // packages.Error is a struct; collect all messages
324
- msgs = append(msgs, e.Error())
325
- }
326
- return nil, fmt.Errorf("package %s has load errors: %s", pkg.PkgPath, strings.Join(msgs, "; "))
327
- }
328
-
329
- pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg, allPackages)
330
- if err != nil {
331
- return nil, fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
332
- }
333
-
334
- if err := pkgCompiler.Compile(ctx); err != nil {
335
- return nil, fmt.Errorf("failed to compile package %s: %w", pkg.PkgPath, err)
336
- }
337
-
338
- c.le.Info(pkg.PkgPath)
339
-
340
- result.CompiledPackages = append(result.CompiledPackages, pkg.PkgPath)
341
- }
342
-
343
- return result, nil
344
- }
345
-
346
- // PackageCompiler is responsible for compiling an entire Go package into
347
- // its TypeScript equivalent. It manages the compilation of individual files
348
- // within the package and determines the output path for the compiled package.
349
- type PackageCompiler struct {
350
- le *logrus.Entry
351
- compilerConf *Config
352
- outputPath string
353
- pkg *packages.Package
354
- allPackages map[string]*packages.Package
355
- }
356
-
357
- // NewPackageCompiler creates a new `PackageCompiler` for a given Go package.
358
- // It initializes the compiler with the necessary configuration, logger, and
359
- // the `packages.Package` data obtained from `golang.org/x/tools/go/packages`.
360
- // It also computes the base output path for the compiled TypeScript files of the package.
361
- func NewPackageCompiler(
362
- le *logrus.Entry,
363
- compilerConf *Config,
364
- pkg *packages.Package,
365
- allPackages map[string]*packages.Package,
366
- ) (*PackageCompiler, error) {
367
- res := &PackageCompiler{
368
- le: le,
369
- pkg: pkg,
370
- compilerConf: compilerConf,
371
- outputPath: ComputeModulePath(compilerConf.OutputPath, pkg.PkgPath),
372
- allPackages: allPackages,
373
- }
374
-
375
- return res, nil
376
- }
377
-
378
- // Compile orchestrates the compilation of all Go files within the package.
379
- //
380
- // It iterates through each syntax file (`ast.File`) of the package,
381
- // determines its relative path for logging, and then invokes `CompileFile`
382
- // to handle the compilation of that specific file.
383
- //
384
- // After compiling all files, it generates an index.ts file that re-exports
385
- // all exported symbols, allowing the package to be imported correctly.
386
- //
387
- // The working directory (`wd`) is used to make file paths in logs more readable.
388
- func (c *PackageCompiler) Compile(ctx context.Context) error {
389
- wd := c.compilerConf.Dir
390
- if wd == "" {
391
- var err error
392
- wd, err = os.Getwd()
393
- if err != nil {
394
- return err
395
- }
396
- }
397
-
398
- // Perform package-level analysis for auto-imports
399
- packageAnalysis := AnalyzePackageImports(c.pkg)
400
-
401
- // Perform comprehensive package-level analysis for code generation
402
- analysis := AnalyzePackageFiles(c.pkg, c.allPackages)
403
-
404
- // Track all compiled files for later generating the index.ts
405
- compiledFiles := make([]string, 0, len(c.pkg.CompiledGoFiles))
406
-
407
- // Compile the files in the package one at a time
408
- for i, f := range c.pkg.Syntax {
409
- fileName := c.pkg.CompiledGoFiles[i]
410
- relWdFileName, err := filepath.Rel(wd, fileName)
411
- if err != nil {
412
- return err
413
- }
414
-
415
- // Check if this is a .pb.go file that should be skipped
416
- baseFileName := filepath.Base(fileName)
417
- if before, ok := strings.CutSuffix(baseFileName, ".pb.go"); ok {
418
- // Check if there's a corresponding .pb.ts file
419
- pbTsFileName := before + ".pb.ts"
420
- packageDir := filepath.Dir(fileName)
421
- pbTsPath := filepath.Join(packageDir, pbTsFileName)
422
-
423
- if _, err := os.Stat(pbTsPath); err == nil {
424
- // .pb.ts file exists, copy it instead of transpiling .pb.go
425
- c.le.WithField("file", relWdFileName).Debug("found .pb.ts file, copying instead of transpiling .pb.go")
426
-
427
- if err := c.copyProtobufTSFile(pbTsPath, pbTsFileName); err != nil {
428
- return fmt.Errorf("failed to copy protobuf .pb.ts file: %w", err)
429
- }
430
-
431
- // Add the .pb file to our compiled files list for index generation
432
- pbFileName := strings.TrimSuffix(baseFileName, ".pb.go") + ".pb"
433
- compiledFiles = append(compiledFiles, pbFileName)
434
- continue // Skip transpiling this .pb.go file
435
- }
436
- }
437
-
438
- // log just the filename
439
- c.le.Debugf("GS: %s", filepath.Base(fileName))
440
- if err := c.CompileFile(ctx, fileName, f, analysis, packageAnalysis); err != nil {
441
- return err
442
- }
443
-
444
- // Add the base filename to our list for the index.ts generation
445
- // Strip .go extension and add .gs
446
- gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
447
- compiledFiles = append(compiledFiles, gsFileName)
448
- }
449
-
450
- // After compiling all files, generate the index.ts file
451
- if err := c.generateIndexFile(compiledFiles); err != nil {
452
- return err
453
- }
454
-
455
- return nil
456
- }
457
-
458
- // generateIndexFile creates an index.ts file in the package output directory
459
- // that re-exports only Go-exported symbols from the compiled TypeScript files.
460
- // This ensures the package can be imported correctly by TypeScript modules
461
- // while maintaining proper Go package boundaries.
462
- func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
463
- indexFilePath := filepath.Join(c.outputPath, "index.ts")
464
-
465
- // Open the file for writing
466
- indexFile, err := os.OpenFile(indexFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644)
467
- if err != nil {
468
- return err
469
- }
470
- defer indexFile.Close() //nolint:errcheck
471
-
472
- // Write selective re-exports for each compiled file
473
- for _, fileName := range compiledFiles {
474
- // Check if this is a protobuf file
475
- if strings.HasSuffix(fileName, ".pb") {
476
- // For protobuf files, add a simple re-export
477
- if err := c.writeProtobufExports(indexFile, fileName); err != nil {
478
- return err
479
- }
480
- continue
481
- }
482
-
483
- // Find which symbols this file exports
484
- var valueSymbols []string
485
- var typeSymbols []string
486
- var structSymbols []string // New: Track structs separately
487
-
488
- // Find the corresponding syntax file
489
- for i, syntax := range c.pkg.Syntax {
490
- syntaxFileName := c.pkg.CompiledGoFiles[i]
491
- syntaxBaseFileName := filepath.Base(syntaxFileName)
492
- syntaxGsFileName := strings.TrimSuffix(syntaxBaseFileName, ".go") + ".gs"
493
-
494
- if syntaxGsFileName != fileName {
495
- continue
496
- }
497
-
498
- // Collect exported symbols from this specific file
499
- for _, decl := range syntax.Decls {
500
- switch d := decl.(type) {
501
- case *ast.FuncDecl:
502
- if d.Recv == nil && d.Name.IsExported() {
503
- valueSymbols = append(valueSymbols, sanitizeIdentifier(d.Name.Name))
504
- } else if d.Recv != nil && len(d.Recv.List) == 1 && d.Name.IsExported() {
505
- recvField := d.Recv.List[0]
506
- recvTypeExpr := recvField.Type
507
- if star, ok := recvTypeExpr.(*ast.StarExpr); ok {
508
- recvTypeExpr = star.X
509
- }
510
- if ident, ok := recvTypeExpr.(*ast.Ident); ok {
511
- typeObj := c.pkg.TypesInfo.ObjectOf(ident)
512
- if typeObj != nil && typeObj.Exported() {
513
- if typeName, ok := typeObj.(*types.TypeName); ok {
514
- underlying := typeName.Type().Underlying()
515
- if _, isStruct := underlying.(*types.Struct); !isStruct {
516
- methodName := sanitizeIdentifier(ident.Name + "_" + d.Name.Name)
517
- valueSymbols = append(valueSymbols, methodName)
518
- }
519
- }
520
- }
521
- }
522
- }
523
- case *ast.GenDecl:
524
- for _, spec := range d.Specs {
525
- switch s := spec.(type) {
526
- case *ast.TypeSpec:
527
- if s.Name.IsExported() {
528
- // Check if this is a struct type
529
- if _, isStruct := s.Type.(*ast.StructType); isStruct {
530
- // Structs become TypeScript classes and need both type and value exports
531
- structSymbols = append(structSymbols, sanitizeIdentifier(s.Name.Name))
532
- } else {
533
- // Other type declarations (interfaces, type definitions, type aliases)
534
- // become TypeScript types and must be exported with "export type"
535
- typeSymbols = append(typeSymbols, sanitizeIdentifier(s.Name.Name))
536
- }
537
- }
538
- case *ast.ValueSpec:
539
- for _, name := range s.Names {
540
- if name.IsExported() {
541
- valueSymbols = append(valueSymbols, sanitizeIdentifier(name.Name))
542
- }
543
- }
544
- }
545
- }
546
- }
547
- }
548
- break
549
- }
550
-
551
- // Write exports if this file has exported symbols
552
- if len(valueSymbols) > 0 {
553
- slices.Sort(valueSymbols)
554
- exportLine := fmt.Sprintf("export { %s } from %q\n",
555
- strings.Join(valueSymbols, ", "), translateGeneratedFileToImportPath(fileName))
556
- if _, err := indexFile.WriteString(exportLine); err != nil {
557
- return err
558
- }
559
- }
560
-
561
- // Write struct exports (both as types and values)
562
- if len(structSymbols) > 0 {
563
- slices.Sort(structSymbols)
564
- // Export classes as values (which makes them available as both types and values in TypeScript)
565
- exportLine := fmt.Sprintf("export { %s } from %q\n",
566
- strings.Join(structSymbols, ", "), translateGeneratedFileToImportPath(fileName))
567
- if _, err := indexFile.WriteString(exportLine); err != nil {
568
- return err
569
- }
570
- }
571
-
572
- if len(typeSymbols) > 0 {
573
- slices.Sort(typeSymbols)
574
- exportLine := fmt.Sprintf("export type { %s } from %q\n",
575
- strings.Join(typeSymbols, ", "), translateGeneratedFileToImportPath(fileName))
576
- if _, err := indexFile.WriteString(exportLine); err != nil {
577
- return err
578
- }
579
- }
580
- }
581
-
582
- return nil
583
- }
584
-
585
- // CompileFile handles the compilation of a single Go source file to TypeScript.
586
- // It uses the pre-computed package-level analysis for accurate TypeScript generation
587
- // (e.g., about varRefing, async functions, defer statements, receiver usage across files).
588
- // Then, it creates a `FileCompiler` instance for the file and invokes its
589
- // `Compile` method to generate the TypeScript code.
590
- func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, analysis *Analysis, packageAnalysis *PackageAnalysis) error {
591
- fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis, packageAnalysis)
592
- if err != nil {
593
- return err
594
- }
595
- return fileCompiler.Compile(ctx)
596
- }
597
-
598
- // FileCompiler is responsible for compiling a single Go source file (`ast.File`)
599
- // into a corresponding TypeScript file. It manages the output file creation,
600
- // initializes the `TSCodeWriter` for TypeScript code generation, and uses a
601
- // `GoToTSCompiler` to translate Go declarations and statements.
602
- type FileCompiler struct {
603
- compilerConfig *Config
604
- codeWriter *TSCodeWriter
605
- pkg *packages.Package
606
- ast *ast.File
607
- fullPath string
608
- Analysis *Analysis
609
- PackageAnalysis *PackageAnalysis
610
- }
611
-
612
- // NewFileCompiler creates a new `FileCompiler` for a specific Go file.
613
- // It takes the global compiler configuration, the Go package information,
614
- // the AST of the file, its full path, and the pre-computed analysis results.
615
- // This setup provides all necessary context for translating the file.
616
- func NewFileCompiler(
617
- compilerConf *Config,
618
- pkg *packages.Package,
619
- astFile *ast.File,
620
- fullPath string,
621
- analysis *Analysis,
622
- packageAnalysis *PackageAnalysis,
623
- ) (*FileCompiler, error) {
624
- return &FileCompiler{
625
- compilerConfig: compilerConf,
626
- pkg: pkg,
627
- ast: astFile,
628
- fullPath: fullPath,
629
- Analysis: analysis,
630
- PackageAnalysis: packageAnalysis,
631
- }, nil
632
- }
633
-
634
- // Compile generates the TypeScript code for the Go file.
635
- // It determines the output TypeScript file path, creates the necessary
636
- // directories, and opens the output file. It then initializes a `TSCodeWriter`
637
- // and a `GoToTSCompiler`. A standard import for the `@goscript/builtin`
638
- // runtime (aliased as `$`) is added, followed by the translation of all
639
- // top-level declarations in the Go file.
640
- func (c *FileCompiler) Compile(ctx context.Context) error {
641
- f := c.ast
642
- pkgPath := c.pkg.PkgPath
643
-
644
- outputFilePath := TranslateGoFilePathToTypescriptFilePath(pkgPath, filepath.Base(c.fullPath))
645
- outputFilePathAbs := filepath.Join(c.compilerConfig.OutputPath, outputFilePath)
646
-
647
- if err := os.MkdirAll(filepath.Dir(outputFilePathAbs), 0o755); err != nil {
648
- return err
649
- }
650
-
651
- of, err := os.OpenFile(outputFilePathAbs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644)
652
- if err != nil {
653
- return err
654
- }
655
- defer of.Close() //nolint:errcheck
656
-
657
- c.codeWriter = NewTSCodeWriter(of)
658
-
659
- // Pass analysis to compiler
660
- goWriter := NewGoToTSCompiler(c.codeWriter, c.pkg, c.Analysis, c.fullPath)
661
-
662
- // Add import for the goscript runtime using namespace import and alias
663
- c.codeWriter.WriteLinef(
664
- "import * as $ from %q",
665
- translateTypescriptModulePathToIndexImportPath("@goscript/builtin"),
666
- )
667
-
668
- // Check if there are any .pb.go files in this package and add imports for them
669
- if err := c.addProtobufImports(); err != nil {
670
- return fmt.Errorf("failed to add protobuf imports: %w", err)
671
- }
672
-
673
- // Generate auto-imports for functions from other files in the same package
674
- currentFileName := strings.TrimSuffix(filepath.Base(c.fullPath), ".go")
675
- if imports := c.PackageAnalysis.FunctionCalls[currentFileName]; imports != nil {
676
- // Sort source files for consistent import order
677
- var sourceFiles []string
678
- for sourceFile := range imports {
679
- sourceFiles = append(sourceFiles, sourceFile)
680
- }
681
- slices.Sort(sourceFiles)
682
-
683
- for _, sourceFile := range sourceFiles {
684
- functions := imports[sourceFile]
685
- if len(functions) > 0 {
686
- // Apply sanitization to function names
687
- var sanitizedFunctions []string
688
- for _, fn := range functions {
689
- sanitizedFunctions = append(sanitizedFunctions, sanitizeIdentifier(fn))
690
- }
691
- // Sort functions for consistent output
692
- slices.Sort(sanitizedFunctions)
693
- c.codeWriter.WriteLinef("import { %s } from %q;",
694
- strings.Join(sanitizedFunctions, ", "), translateGeneratedGoFileToImportPath(sourceFile))
695
- }
696
- }
697
- }
698
-
699
- // Generate auto-imports for types from other files in the same package
700
- if typeImports := c.PackageAnalysis.TypeCalls[currentFileName]; typeImports != nil {
701
- // Sort source files for consistent import order
702
- var sourceFiles []string
703
- for sourceFile := range typeImports {
704
- sourceFiles = append(sourceFiles, sourceFile)
705
- }
706
- slices.Sort(sourceFiles)
707
-
708
- for _, sourceFile := range sourceFiles {
709
- typeImports := typeImports[sourceFile]
710
- if len(typeImports) > 0 {
711
- // Filter out protobuf types - they should be imported from .pb.ts files, not .gs.ts files
712
- var nonProtobufTypes []string
713
- for _, typeName := range typeImports {
714
- // Check if this type is a protobuf type by looking at its type info
715
- isProtobuf := false
716
- if typeObj := c.pkg.Types.Scope().Lookup(typeName); typeObj != nil {
717
- objType := typeObj.Type()
718
- if namedType, ok := objType.(*types.Named); ok {
719
- // Use the same detection logic as codegen
720
- if goWriter.isProtobufType(namedType) {
721
- isProtobuf = true
722
- }
723
- }
724
- }
725
- if !isProtobuf {
726
- nonProtobufTypes = append(nonProtobufTypes, typeName)
727
- }
728
- }
729
-
730
- if len(nonProtobufTypes) > 0 {
731
- // Apply sanitization to type names
732
- var sanitizedTypes []string
733
- for _, typeName := range nonProtobufTypes {
734
- sanitizedTypes = append(sanitizedTypes, sanitizeIdentifier(typeName))
735
- }
736
- // Sort types for consistent output
737
- slices.Sort(sanitizedTypes)
738
- c.codeWriter.WriteLinef("import { %s } from %q;",
739
- strings.Join(sanitizedTypes, ", "), translateGeneratedGoFileToImportPath(sourceFile))
740
- }
741
- }
742
- }
743
- }
744
-
745
- // Generate auto-imports for variables from other files in the same package
746
- if varImports := c.PackageAnalysis.VariableCalls[currentFileName]; varImports != nil {
747
- // Sort source files for consistent import order
748
- var sourceFiles []string
749
- for sourceFile := range varImports {
750
- sourceFiles = append(sourceFiles, sourceFile)
751
- }
752
- slices.Sort(sourceFiles)
753
-
754
- for _, sourceFile := range sourceFiles {
755
- variables := varImports[sourceFile]
756
- if len(variables) > 0 {
757
- // Apply sanitization to variable names
758
- var sanitizedVariables []string
759
- for _, varName := range variables {
760
- sanitizedVariables = append(sanitizedVariables, sanitizeIdentifier(varName))
761
- }
762
- // Sort variables for consistent output
763
- slices.Sort(sanitizedVariables)
764
- c.codeWriter.WriteLinef("import { %s } from %q;",
765
- strings.Join(sanitizedVariables, ", "), translateGeneratedGoFileToImportPath(sourceFile))
766
- }
767
- }
768
- }
769
-
770
- // Write synthetic imports (for promoted methods from embedded structs)
771
- // Use per-file synthetic imports to avoid adding unused imports
772
- if syntheticImports := c.Analysis.SyntheticImportsPerFile[c.fullPath]; syntheticImports != nil {
773
- // Sort by package name for consistent output
774
- var syntheticPkgNames []string
775
- for pkgName := range syntheticImports {
776
- syntheticPkgNames = append(syntheticPkgNames, pkgName)
777
- }
778
- slices.Sort(syntheticPkgNames)
779
- for _, pkgName := range syntheticPkgNames {
780
- imp := syntheticImports[pkgName]
781
- c.codeWriter.WriteImport(pkgName, translateTypescriptModulePathToIndexImportPath(imp.importPath))
782
- }
783
- }
784
-
785
- c.codeWriter.WriteLine("") // Add a newline after imports
786
-
787
- if err := goWriter.WriteDecls(f.Decls); err != nil {
788
- return fmt.Errorf("failed to write declarations: %w", err)
789
- }
790
-
791
- return nil
792
- }
793
-
794
- // GoToTSCompiler is the core component responsible for translating Go AST nodes
795
- // and type information into TypeScript code. It uses a `TSCodeWriter` to output
796
- // the generated TypeScript and relies on `Analysis` data to make informed
797
- // decisions about code generation (e.g., varRefing, async behavior).
798
- type GoToTSCompiler struct {
799
- tsw *TSCodeWriter
800
- pkg *packages.Package
801
-
802
- analysis *Analysis
803
-
804
- // currentFilePath is the path of the file being compiled
805
- // Used for looking up per-file synthetic imports
806
- currentFilePath string
807
-
808
- // Context flags
809
- insideAddressOf bool // true when processing operand of & operator
810
-
811
- // renamedVars tracks variables that have been renamed to avoid type shadowing
812
- // Key: types.Object of the original variable, Value: new name to use
813
- renamedVars map[types.Object]string
814
- }
815
-
816
- // NewGoToTSCompiler creates a new GoToTSCompiler with a TSCodeWriter for output,
817
- // Go package information, pre-computed analysis results, and the current file path.
818
- func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis, filePath string) *GoToTSCompiler {
819
- return &GoToTSCompiler{
820
- tsw: tsw,
821
- pkg: pkg,
822
- analysis: analysis,
823
- currentFilePath: filePath,
824
- renamedVars: make(map[types.Object]string),
825
- }
826
- }
827
-
828
- // objectOfIdent returns the types.Object associated with the identifier.
829
- // It checks Uses first, then Defs, and returns nil if neither is found.
830
- func (c *GoToTSCompiler) objectOfIdent(ident *ast.Ident) types.Object {
831
- if ident == nil || c.pkg == nil || c.pkg.TypesInfo == nil {
832
- return nil
833
- }
834
- if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
835
- return obj
836
- }
837
- if obj := c.pkg.TypesInfo.Defs[ident]; obj != nil {
838
- return obj
839
- }
840
- return nil
841
- }
842
-
843
- // getDeterministicID generates a deterministic unique ID based on file position
844
- // This replaces the non-deterministic Pos() values to ensure reproducible builds
845
- func (c *GoToTSCompiler) getDeterministicID(pos token.Pos) string {
846
- if !pos.IsValid() {
847
- return "0000"
848
- }
849
-
850
- // Get file position information
851
- position := c.pkg.Fset.Position(pos)
852
-
853
- // Use package path + base filename + line + column for deterministic hashing
854
- // This avoids absolute path differences between build environments
855
- baseFilename := filepath.Base(position.Filename)
856
- if baseFilename == "" {
857
- baseFilename = "unknown"
858
- }
859
-
860
- packagePath := c.pkg.PkgPath
861
- if packagePath == "" {
862
- packagePath = "main"
863
- }
864
-
865
- // Create a string that uniquely identifies this position using only relative/stable info
866
- positionStr := fmt.Sprintf("%s:%s:%d:%d", packagePath, baseFilename, position.Line, position.Column)
867
-
868
- // Hash the position string with SHA256
869
- hash := sha256.Sum256([]byte(positionStr))
870
-
871
- // Convert to hex and take the last 4 characters (lowercase)
872
- hexStr := hex.EncodeToString(hash[:])
873
- return hexStr[len(hexStr)-4:]
874
- }
875
-
876
- // --- Exported Node-Specific Writers ---
877
-
878
- // WriteIdent translates a Go identifier (`ast.Ident`) used as a value (e.g.,
879
- // variable, function name) into its TypeScript equivalent.
880
- // - If the identifier is `nil`, it writes `null`.
881
- // - If the identifier refers to a constant, it writes the constant's evaluated value.
882
- // - Otherwise, it writes the identifier's name.
883
- // - If `accessVarRefedValue` is true and the analysis (`c.analysis.NeedsVarRefAccess`)
884
- // indicates the variable is variable referenced, `.value` is appended to access the contained value.
885
- //
886
- // This function relies on `go/types` (`TypesInfo.Uses` or `Defs`) to resolve
887
- // the identifier and the `Analysis` data to determine varRefing needs.
888
- func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
889
- if exp.Name == "nil" {
890
- c.tsw.WriteLiterally("null")
891
- return
892
- }
893
-
894
- // Check if this identifier has a pre-computed mapping (e.g., wrapper function receiver)
895
- if mappedName := c.analysis.GetIdentifierMapping(exp); mappedName != "" {
896
- c.tsw.WriteLiterally(c.sanitizeIdentifier(mappedName))
897
- return
898
- }
899
-
900
- // Use TypesInfo to find the object associated with the identifier
901
- obj := c.objectOfIdent(exp)
902
-
903
- // Check if this variable has been renamed to avoid type shadowing
904
- if obj != nil {
905
- if renamedName, ok := c.renamedVars[obj]; ok {
906
- c.tsw.WriteLiterally(c.sanitizeIdentifier(renamedName))
907
- // Determine if we need to access .value based on analysis data
908
- if accessVarRefedValue && c.analysis.NeedsVarRefAccess(obj) {
909
- c.tsw.WriteLiterally("!.value")
910
- }
911
- return
912
- }
913
- }
914
-
915
- // Check if this identifier refers to a constant
916
- if obj != nil {
917
- if constObj, isConst := obj.(*types.Const); isConst {
918
- // Evaluate constants to literals in these cases:
919
- // 1. Predeclared constants (like iota, true, false) - these have nil package
920
- // 2. Constants from the current package that are computed expressions
921
- // (like iota-based constants or mathematical expressions)
922
- //
923
- // For simple assignments from imported constants (const x = pkg.Y),
924
- // preserve the variable name for better readability.
925
-
926
- if constObj.Pkg() == nil {
927
- // Predeclared constants - always evaluate to literals
928
- c.writeConstantValue(constObj)
929
- return
930
- }
931
-
932
- if constObj.Pkg() == c.pkg.Types {
933
- // This is a constant from the current package.
934
- // Check if it's a simple assignment by looking at the constant value:
935
- // If the constant has the same name as an identifier in the import,
936
- // it's likely a simple assignment and we should preserve the name.
937
- // Otherwise, evaluate to literal (for computed expressions like iota).
938
-
939
- // For now, let's be conservative and evaluate current package constants
940
- // to literals to maintain compatibility with existing behavior.
941
- // This handles iota-based constants correctly.
942
- c.writeConstantValue(constObj)
943
- return
944
- }
945
- }
946
- }
947
-
948
- // Write the identifier name first, sanitizing if it's a reserved word
949
- c.tsw.WriteLiterally(c.sanitizeIdentifier(exp.Name))
950
-
951
- // Determine if we need to access .value based on analysis data
952
- if obj != nil && accessVarRefedValue && c.analysis.NeedsVarRefAccess(obj) {
953
- c.tsw.WriteLiterally("!.value")
954
- }
955
- }
956
-
957
- // WriteCaseClause translates a Go `case` clause (`ast.CaseClause`) from within
958
- // a `switch` statement into its TypeScript `case` or `default` equivalent.
959
- // - If `exp.List` is nil, it's a `default` case, written as `default:`.
960
- // - If `exp.List` is not nil, it's a `case` with one or more match expressions.
961
- // It's written as `case expr1_ts, expr2_ts, ... :`. (Note: Go's `case`
962
- // doesn't allow multiple expressions this way, but TypeScript does. This code
963
- // implies Go's fallthrough is not directly modeled here but rather by explicit
964
- // `break`s, and each Go `case` becomes one or more TS `case` labels if needed,
965
- // though current implementation writes them comma-separated which is valid TS syntax).
966
- // - The body of the case (`exp.Body`) is translated statement by statement using `WriteStmt`.
967
- // - A `break;` statement is automatically added at the end of the TypeScript case
968
- // body, because Go `switch` cases have implicit breaks, whereas TypeScript
969
- // cases fall through by default.
970
- func (c *GoToTSCompiler) WriteCaseClause(exp *ast.CaseClause) error {
971
- if exp.List == nil {
972
- // Default case
973
- c.tsw.WriteLiterally("default:")
974
- c.tsw.WriteLine(" {")
975
- } else {
976
- // Case with expressions
977
- // For Go's `case expr1, expr2:`, we translate to:
978
- // case expr1:
979
- // case expr2:
980
- // ... body ...
981
- // break
982
- for i, caseExpr := range exp.List {
983
- c.tsw.WriteLiterally("case ")
984
- if err := c.WriteValueExpr(caseExpr); err != nil {
985
- return fmt.Errorf("failed to write case clause expression: %w", err)
986
- }
987
- c.tsw.WriteLiterally(":")
988
- // Only add opening brace after the last case label
989
- if i == len(exp.List)-1 {
990
- c.tsw.WriteLine(" {")
991
- } else {
992
- c.tsw.WriteLine("")
993
- }
994
- }
995
- }
996
-
997
- // The body is written once, after all case labels for this clause.
998
- // Wrap in block to provide Go-like case scope semantics.
999
- c.tsw.Indent(1)
1000
- for _, stmt := range exp.Body {
1001
- if err := c.WriteStmt(stmt); err != nil {
1002
- return fmt.Errorf("failed to write statement in case clause body: %w", err)
1003
- }
1004
- }
1005
- // Add break statement only if the case body doesn't end with a terminating statement
1006
- // (return, panic, continue, break, goto, fallthrough). Go's switch has implicit breaks,
1007
- // but TS needs explicit break - however, adding break after return is unreachable code.
1008
- if !endsWithTerminatingStmt(exp.Body) {
1009
- c.tsw.WriteLine("break")
1010
- }
1011
- c.tsw.Indent(-1)
1012
- c.tsw.WriteLine("}")
1013
- return nil
1014
- }
1015
-
1016
- // writeChannelReceiveWithOk handles the specific Go assignment pattern
1017
- // `value, ok := <-channel` (or `:=`).
1018
- // It translates this into a TypeScript destructuring assignment/declaration
1019
- // from the result of `await channel_ts.receiveWithOk()`.
1020
- // The `receiveWithOk()` runtime method is expected to return an object like
1021
- // `{ value: receivedValue, ok: boolean }`.
1022
- //
1023
- // - If `tok` is `token.DEFINE` (for `:=`), it generates `const { value: valueName, ok: okName } = ...`.
1024
- // - Otherwise (for `=`), it generates `{ value: valueName, ok: okName } = ...` (if not a declaration).
1025
- // - Blank identifiers (`_`) on the LHS are handled:
1026
- // - If `value` is blank: `const { ok: okName } = ...` or `{ ok: okName } = ...`.
1027
- // - If `ok` is blank: `const { value: valueName } = ...` or `{ value: valueName } = ...`.
1028
- // - If both are blank, it simply writes `await channel_ts.receiveWithOk()` to
1029
- // execute the receive for its potential side effects (though `receiveWithOk`
1030
- // is primarily for its return values) and discards the result.
1031
- //
1032
- // This ensures that the Go channel receive with existence check is correctly
1033
- // mapped to the asynchronous TypeScript channel helper.
1034
- func (c *GoToTSCompiler) writeChannelReceiveWithOk(lhs []ast.Expr, unaryExpr *ast.UnaryExpr, tok token.Token) error {
1035
- // Ensure LHS has exactly two expressions
1036
- if len(lhs) != 2 {
1037
- return fmt.Errorf("internal error: writeChannelReceiveWithOk called with %d LHS expressions", len(lhs))
1038
- }
1039
-
1040
- // Get variable names, handling blank identifiers
1041
- valueIsBlank := false
1042
- okIsBlank := false
1043
- var valueName string
1044
- var okName string
1045
-
1046
- if valIdent, ok := lhs[0].(*ast.Ident); ok {
1047
- if valIdent.Name == "_" {
1048
- valueIsBlank = true
1049
- } else {
1050
- valueName = valIdent.Name
1051
- }
1052
- } else {
1053
- return fmt.Errorf("unhandled LHS expression type for value in channel receive: %T", lhs[0])
1054
- }
1055
-
1056
- if okIdent, ok := lhs[1].(*ast.Ident); ok {
1057
- if okIdent.Name == "_" {
1058
- okIsBlank = true
1059
- } else {
1060
- okName = okIdent.Name
1061
- }
1062
- } else {
1063
- return fmt.Errorf("unhandled LHS expression type for ok in channel receive: %T", lhs[1])
1064
- }
1065
-
1066
- // Generate destructuring assignment/declaration for val, ok := <-channel
1067
- keyword := ""
1068
- if tok == token.DEFINE {
1069
- keyword = "const " // Use const for destructuring declaration
1070
- }
1071
-
1072
- // Build the destructuring pattern, handling blank identifiers correctly for TS
1073
- patternParts := []string{}
1074
- if !valueIsBlank {
1075
- // Map the 'value' field to the Go variable name
1076
- patternParts = append(patternParts, fmt.Sprintf("value: %s", valueName))
1077
- } else {
1078
- // If both are blank, just await the call and return
1079
- if okIsBlank {
1080
- c.tsw.WriteLiterally("await $.chanRecvWithOk(")
1081
- if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
1082
- return fmt.Errorf("failed to write channel expression in receive: %w", err)
1083
- }
1084
- c.tsw.WriteLiterally(")")
1085
- c.tsw.WriteLine("")
1086
- return nil // Nothing to assign
1087
- }
1088
- // Only value is blank, need to map 'ok' property
1089
- patternParts = append(patternParts, fmt.Sprintf("ok: %s", okName))
1090
- }
1091
-
1092
- if !okIsBlank && !valueIsBlank { // Both are present
1093
- patternParts = append(patternParts, fmt.Sprintf("ok: %s", okName))
1094
- }
1095
- // If both are blank, patternParts remains empty, handled earlier.
1096
-
1097
- destructuringPattern := fmt.Sprintf("{ %s }", strings.Join(patternParts, ", "))
1098
-
1099
- // Write the destructuring assignment/declaration
1100
- c.tsw.WriteLiterally(keyword) // "const " or ""
1101
- c.tsw.WriteLiterally(destructuringPattern)
1102
- c.tsw.WriteLiterally(" = await $.chanRecvWithOk(")
1103
- if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
1104
- return fmt.Errorf("failed to write channel expression in receive: %w", err)
1105
- }
1106
- c.tsw.WriteLiterally(")")
1107
- c.tsw.WriteLine("")
1108
-
1109
- return nil
1110
- }
1111
-
1112
- // WriteDoc translates a Go comment group (`ast.CommentGroup`) into TypeScript comments,
1113
- // preserving the original style (line `//` or block `/* ... */`).
1114
- // - If `doc` is nil, it does nothing.
1115
- // - It iterates through each `ast.Comment` in the group.
1116
- // - If a comment starts with `//`, it's written as a TypeScript line comment.
1117
- // - If a comment starts with `/*`, it's written as a TypeScript block comment:
1118
- // - Single-line block comments (`/* comment */`) are kept on one line.
1119
- // - Multi-line block comments are formatted with `/*` on its own line,
1120
- // each content line prefixed with ` * `, and ` */` on its own line.
1121
- //
1122
- // This function helps maintain documentation and explanatory comments from the
1123
- // Go source in the generated TypeScript code.
1124
- func (c *GoToTSCompiler) WriteDoc(doc *ast.CommentGroup) {
1125
- if doc == nil {
1126
- return
1127
- }
1128
-
1129
- for _, comment := range doc.List {
1130
- // Preserve original comment style (// or /*)
1131
- if strings.HasPrefix(comment.Text, "//") {
1132
- c.tsw.WriteLine(comment.Text)
1133
- } else if after, ok := strings.CutPrefix(comment.Text, "/*"); ok {
1134
- // Write block comments potentially spanning multiple lines
1135
- // Remove /* and */, then split by newline
1136
- content := strings.TrimSuffix(after, "*/")
1137
- lines := strings.Split(content, "\n") // Use \n as Split expects a separator string
1138
-
1139
- if len(lines) == 1 && !strings.Contains(lines[0], "\n") { // Check again for internal newlines just in case
1140
- // Keep single-line block comments on one line
1141
- c.tsw.WriteLinef("/*%s*/", lines[0])
1142
- } else {
1143
- // Write multi-line block comments
1144
- c.tsw.WriteLine("/*")
1145
- for _, line := range lines {
1146
- // WriteLine handles indentation preamble automatically
1147
- c.tsw.WriteLine(" *" + line) // Add conventional * prefix
1148
- }
1149
- c.tsw.WriteLine(" */")
1150
- }
1151
- } else {
1152
- // Should not happen for valid Go comments, but handle defensively
1153
- c.tsw.WriteCommentLine(" Unknown comment format: " + comment.Text)
1154
- }
1155
- }
1156
- }
1157
-
1158
- // sanitizeIdentifier is a method wrapper around the package-level sanitizeIdentifier function
1159
- func (c *GoToTSCompiler) sanitizeIdentifier(name string) string {
1160
- return sanitizeIdentifier(name)
1161
- }
1162
-
1163
- // writeConstantValue writes the evaluated value of a Go constant to TypeScript.
1164
- // It handles different constant types (integer, float, string, boolean, complex)
1165
- // and writes the appropriate TypeScript literal.
1166
- func (c *GoToTSCompiler) writeConstantValue(constObj *types.Const) {
1167
- val := constObj.Val()
1168
-
1169
- switch val.Kind() {
1170
- case constant.Int:
1171
- // For integer constants, write the string representation
1172
- c.tsw.WriteLiterally(val.String())
1173
- case constant.Float:
1174
- // For float constants, write the string representation
1175
- c.tsw.WriteLiterally(val.String())
1176
- case constant.String:
1177
- // For string constants, write as a quoted string literal
1178
- // Use constant.StringVal to get the full string value without truncation,
1179
- // then manually add quotes since StringVal returns the unquoted string
1180
- stringValue := constant.StringVal(val)
1181
- c.tsw.WriteLiterallyf("%q", stringValue) // %q adds proper quotes and escaping
1182
- case constant.Bool:
1183
- // For boolean constants, write true/false
1184
- if constant.BoolVal(val) {
1185
- c.tsw.WriteLiterally("true")
1186
- } else {
1187
- c.tsw.WriteLiterally("false")
1188
- }
1189
- case constant.Complex:
1190
- // For complex constants, we need to handle them specially
1191
- // For now, write as a comment indicating unsupported
1192
- c.tsw.WriteLiterally("/* complex constant: " + val.String() + " */")
1193
- default:
1194
- // For unknown constant types, write as a comment
1195
- c.tsw.WriteLiterally("/* unknown constant: " + val.String() + " */")
1196
- }
1197
- }
1198
-
1199
- // copyEmbeddedPackage recursively copies files from an embedded FS path to a filesystem directory.
1200
- // It handles both regular files and directories, but only copies .gs.ts and .ts files.
1201
- // It preserves existing subdirectories that aren't being overwritten.
1202
- func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) error {
1203
- // Create the output path if it doesn't exist
1204
- if err := os.MkdirAll(outputPath, 0o755); err != nil {
1205
- return fmt.Errorf("failed to create output directory %s: %w", outputPath, err)
1206
- }
1207
-
1208
- // List the entries in the embedded path
1209
- entries, err := gs.GsOverrides.ReadDir(embeddedPath)
1210
- if err != nil {
1211
- return fmt.Errorf("failed to read embedded directory %s: %w", embeddedPath, err)
1212
- }
1213
-
1214
- // Process each entry
1215
- for _, entry := range entries {
1216
- entryPath := filepath.Join(embeddedPath, entry.Name())
1217
- outputEntryPath := filepath.Join(outputPath, entry.Name())
1218
-
1219
- if entry.IsDir() {
1220
- // Create the output directory
1221
- if err := os.MkdirAll(outputEntryPath, 0o755); err != nil {
1222
- return fmt.Errorf("failed to create output directory %s: %w", outputEntryPath, err)
1223
- }
1224
-
1225
- // Recursively copy the directory contents
1226
- if err := c.copyEmbeddedPackage(entryPath, outputEntryPath); err != nil {
1227
- return err
1228
- }
1229
- } else {
1230
- // Only copy .gs.ts and .ts files, skip .go files and others
1231
- fileName := entry.Name()
1232
- if !strings.HasSuffix(fileName, ".gs.ts") && !strings.HasSuffix(fileName, ".ts") {
1233
- // c.le.Debugf("Skipping non-TypeScript file: %s", fileName)
1234
- continue
1235
- }
1236
-
1237
- // Skip .test.ts files
1238
- if strings.HasSuffix(fileName, ".test.ts") {
1239
- // c.le.Debugf("Skipping test file: %s", fileName)
1240
- continue
1241
- }
1242
-
1243
- // Remove existing file if it exists (but preserve directories)
1244
- if stat, err := os.Stat(outputEntryPath); err == nil && !stat.IsDir() {
1245
- if err := os.Remove(outputEntryPath); err != nil {
1246
- return fmt.Errorf("failed to remove existing file %s: %w", outputEntryPath, err)
1247
- }
1248
- }
1249
-
1250
- // Read the file content from the embedded FS
1251
- content, err := gs.GsOverrides.ReadFile(entryPath)
1252
- if err != nil {
1253
- return fmt.Errorf("failed to read embedded file %s: %w", entryPath, err)
1254
- }
1255
-
1256
- // Write the content to the output file
1257
- if err := os.WriteFile(outputEntryPath, content, 0o644); err != nil {
1258
- return fmt.Errorf("failed to write file %s: %w", outputEntryPath, err)
1259
- }
1260
- }
1261
- }
1262
-
1263
- return nil
1264
- }
1265
-
1266
- // GsPackageMetadata holds metadata about a gs/ package
1267
- type GsPackageMetadata struct {
1268
- // Dependencies lists the import paths that this gs/ package requires
1269
- Dependencies []string `json:"dependencies,omitempty"`
1270
- AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
1271
- }
1272
-
1273
- // ReadGsPackageMetadata reads dependency metadata from meta.json file in a gs/ package
1274
- func (c *Compiler) ReadGsPackageMetadata(gsSourcePath string) (*GsPackageMetadata, error) {
1275
- metadata := &GsPackageMetadata{
1276
- Dependencies: []string{},
1277
- AsyncMethods: make(map[string]bool),
1278
- }
1279
-
1280
- // Try to read meta.json file
1281
- metaFilePath := filepath.Join(gsSourcePath, "meta.json")
1282
- content, err := gs.GsOverrides.ReadFile(metaFilePath)
1283
- if err != nil {
1284
- // Only treat missing file as "no metadata"; surface other errors
1285
- if os.IsNotExist(err) {
1286
- return metadata, nil
1287
- }
1288
- return nil, fmt.Errorf("failed to read meta.json in %s: %w", gsSourcePath, err)
1289
- }
1290
-
1291
- // Parse the JSON content
1292
- if err := json.Unmarshal(content, metadata); err != nil {
1293
- return metadata, fmt.Errorf("failed to parse meta.json in %s: %w", gsSourcePath, err)
1294
- }
1295
-
1296
- return metadata, nil
1297
- }
1298
-
1299
- // copyGsPackageWithDependencies copies a gs/ package and all its dependencies recursively
1300
- // It tracks already processed packages to avoid infinite loops and duplicate work
1301
- func (c *Compiler) copyGsPackageWithDependencies(packagePath string, processedPackages map[string]bool, result *CompilationResult) error {
1302
- // Check if we've already processed this package
1303
- if processedPackages[packagePath] {
1304
- return nil
1305
- }
1306
-
1307
- // Mark this package as being processed
1308
- processedPackages[packagePath] = true
1309
-
1310
- gsSourcePath := "gs/" + packagePath
1311
-
1312
- // Check if the gs package actually exists
1313
- _, gsErr := gs.GsOverrides.ReadDir(gsSourcePath)
1314
- if gsErr != nil {
1315
- if os.IsNotExist(gsErr) {
1316
- c.le.Debugf("gs package %s does not exist, skipping", packagePath)
1317
- return nil
1318
- }
1319
- return gsErr
1320
- }
1321
-
1322
- // Read metadata to get dependencies
1323
- metadata, err := c.ReadGsPackageMetadata(gsSourcePath)
1324
- if err != nil {
1325
- // Surface metadata errors instead of silently continuing
1326
- return fmt.Errorf("failed to read metadata for gs package %s: %w", packagePath, err)
1327
- }
1328
-
1329
- // Log dependencies if any are found
1330
- /*
1331
- if len(metadata.Dependencies) > 0 {
1332
- c.le.Debugf("Package %s has dependencies: %v", packagePath, metadata.Dependencies)
1333
- }
1334
- */
1335
-
1336
- // First, recursively process all dependencies
1337
- for _, depPath := range metadata.Dependencies {
1338
- if err := c.copyGsPackageWithDependencies(depPath, processedPackages, result); err != nil {
1339
- return fmt.Errorf("failed to copy dependency %s of package %s: %w", depPath, packagePath, err)
1340
- }
1341
- }
1342
-
1343
- // Now copy the package itself
1344
- // c.le.Debugf("Copying handwritten package %s to output directory", packagePath)
1345
-
1346
- // Compute output path for this package
1347
- outputPath := ComputeModulePath(c.config.OutputPath, packagePath)
1348
-
1349
- // Create the output directory
1350
- if err := os.MkdirAll(outputPath, 0o755); err != nil {
1351
- return fmt.Errorf("failed to create output directory for %s: %w", packagePath, err)
32
+ if err := ctx.Err(); err != nil {
33
+ return nil, err
1352
34
  }
1353
35
 
1354
- // Copy files from embedded FS to output directory
1355
- if err := c.copyEmbeddedPackage(gsSourcePath, outputPath); err != nil {
1356
- return fmt.Errorf("failed to copy embedded package %s: %w", packagePath, err)
36
+ if c.le != nil {
37
+ c.le.Debugf("goscript v2 compile request: %v", patterns)
1357
38
  }
1358
-
1359
- result.CopiedPackages = append(result.CopiedPackages, packagePath)
1360
- return nil
39
+ request := c.service.RequestOwner().NewRequest(c.config, patterns)
40
+ return c.service.Compile(ctx, request)
1361
41
  }