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
@@ -0,0 +1,450 @@
1
+ package compiler
2
+
3
+ import (
4
+ "context"
5
+ "go/ast"
6
+ "go/types"
7
+ "path"
8
+ "regexp"
9
+ "slices"
10
+ "strings"
11
+
12
+ "github.com/pkg/errors"
13
+ )
14
+
15
+ var (
16
+ overrideExportDeclarationPattern = regexp.MustCompile(`(?m)^\s*export\s+(?:async\s+)?(const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][A-Za-z0-9_$]*)`)
17
+ overrideLocalDeclarationPattern = regexp.MustCompile(`(?m)^\s*(?:export\s+)?(?:async\s+)?(const|let|var|function|class|interface|type|enum)\s+([A-Za-z_$][A-Za-z0-9_$]*)`)
18
+ overrideImportNamedPattern = regexp.MustCompile(`(?s)import\s+(type\s+)?\{([^}]*)\}\s+from\s+['"][^'"]+['"]`)
19
+ overrideExportNamedPattern = regexp.MustCompile(`(?s)export\s+(type\s+)?\{([^}]*)\}\s*(?:from\s+['"]([^'"]+)['"])?`)
20
+ overrideExportAllPattern = regexp.MustCompile(`(?m)^\s*export\s+\*\s+from\s+['"]([^'"]+)['"]`)
21
+ )
22
+
23
+ type goPackageExport struct {
24
+ name string
25
+ requiresValue bool
26
+ }
27
+
28
+ type typeScriptExport struct {
29
+ value bool
30
+ typ bool
31
+ }
32
+
33
+ type namedTypeScriptExport struct {
34
+ source string
35
+ target string
36
+ typeOnly bool
37
+ }
38
+
39
+ // OverrideParityVerifier owns compiler-side parity checks for ledgered overrides.
40
+ type OverrideParityVerifier struct{}
41
+
42
+ // NewOverrideParityVerifier creates the override parity verifier.
43
+ func NewOverrideParityVerifier() *OverrideParityVerifier {
44
+ return &OverrideParityVerifier{}
45
+ }
46
+
47
+ // Verify checks ledgered override packages against JS/WASM Go exports and
48
+ // effective TypeScript package exports.
49
+ func (v *OverrideParityVerifier) Verify(
50
+ ctx context.Context,
51
+ graph *PackageGraph,
52
+ facts *OverrideFacts,
53
+ ) []Diagnostic {
54
+ if err := ctx.Err(); err != nil {
55
+ return []Diagnostic{contextCanceledDiagnostic(err)}
56
+ }
57
+ if graph == nil || facts == nil {
58
+ return nil
59
+ }
60
+
61
+ var diagnostics []Diagnostic
62
+ for _, node := range graph.Nodes {
63
+ if err := ctx.Err(); err != nil {
64
+ return append(diagnostics, contextCanceledDiagnostic(err))
65
+ }
66
+ if node == nil || !node.OverrideCandidate {
67
+ continue
68
+ }
69
+ ledger := facts.parityLedger(node.PkgPath)
70
+ if !ledger.Strict && len(ledger.Symbols) == 0 {
71
+ continue
72
+ }
73
+ pkg := graph.packagesByPath[node.PkgPath]
74
+ if pkg == nil || pkg.Types == nil {
75
+ diagnostics = append(diagnostics, Diagnostic{
76
+ Severity: DiagnosticSeverityError,
77
+ Code: "goscript/overrides:parity-no-package",
78
+ Message: "override parity verification requires typed Go package facts",
79
+ Detail: node.PkgPath,
80
+ })
81
+ continue
82
+ }
83
+ exports, err := facts.effectiveTypeScriptExports(node.PkgPath)
84
+ if err != nil {
85
+ diagnostics = append(diagnostics, Diagnostic{
86
+ Severity: DiagnosticSeverityError,
87
+ Code: "goscript/overrides:parity-export-scan",
88
+ Message: "override parity verification failed to scan TypeScript exports",
89
+ Detail: node.PkgPath + ": " + err.Error(),
90
+ })
91
+ continue
92
+ }
93
+ diagnostics = append(diagnostics, verifyOverrideParityPackage(node.PkgPath, pkg.Types, ledger, exports)...)
94
+ }
95
+ diagnostics = append(diagnostics, verifyBlockedOverrideUses(graph, facts)...)
96
+ return diagnostics
97
+ }
98
+
99
+ // VerifyNoDeferred reports transient parity entries that remain in packages
100
+ // whose parity surface is expected to be closed.
101
+ func (v *OverrideParityVerifier) VerifyNoDeferred(facts *OverrideFacts, pkgPaths ...string) []Diagnostic {
102
+ if facts == nil {
103
+ return nil
104
+ }
105
+ var diagnostics []Diagnostic
106
+ for _, pkgPath := range pkgPaths {
107
+ ledger := facts.parityLedger(pkgPath)
108
+ symbols := make([]string, 0, len(ledger.Symbols))
109
+ for symbol := range ledger.Symbols {
110
+ symbols = append(symbols, symbol)
111
+ }
112
+ slices.Sort(symbols)
113
+ for _, symbol := range symbols {
114
+ entry := ledger.Symbols[symbol]
115
+ if entry.Status != overrideParityStatusDeferred {
116
+ continue
117
+ }
118
+ diagnostics = append(diagnostics, Diagnostic{
119
+ Severity: DiagnosticSeverityError,
120
+ Code: "goscript/overrides:parity-deferred",
121
+ Message: "override parity ledger still contains a transient deferred symbol",
122
+ Detail: pkgPath + "." + symbol,
123
+ })
124
+ }
125
+ }
126
+ return diagnostics
127
+ }
128
+
129
+ func verifyOverrideParityPackage(
130
+ pkgPath string,
131
+ goPkg *types.Package,
132
+ ledger overrideParityLedger,
133
+ tsExports map[string]typeScriptExport,
134
+ ) []Diagnostic {
135
+ goExports := exportedPackageSymbols(goPkg)
136
+ goExportSet := make(map[string]bool, len(goExports))
137
+ var diagnostics []Diagnostic
138
+ for _, symbol := range goExports {
139
+ goExportSet[symbol.name] = true
140
+ entry, ok := ledger.Symbols[symbol.name]
141
+ if ledger.Strict && !ok {
142
+ diagnostics = append(diagnostics, Diagnostic{
143
+ Severity: DiagnosticSeverityError,
144
+ Code: "goscript/overrides:parity-unclassified",
145
+ Message: "Go export is missing from override parity ledger",
146
+ Detail: pkgPath + "." + symbol.name,
147
+ })
148
+ continue
149
+ }
150
+ if !ok {
151
+ continue
152
+ }
153
+ if entry.Status.requiresExport() && !symbol.satisfiedBy(tsExports[symbol.name]) {
154
+ diagnostics = append(diagnostics, Diagnostic{
155
+ Severity: DiagnosticSeverityError,
156
+ Code: "goscript/overrides:parity-missing-export",
157
+ Message: "override parity ledger requires a TypeScript export that is missing",
158
+ Detail: pkgPath + "." + symbol.name + " is classified as " + string(entry.Status),
159
+ })
160
+ }
161
+ if entry.Status.forbidsExport() && tsExports[symbol.name].present() {
162
+ diagnostics = append(diagnostics, Diagnostic{
163
+ Severity: DiagnosticSeverityError,
164
+ Code: "goscript/overrides:parity-unexpected-export",
165
+ Message: "override parity ledger marks a Go export blocked, but TypeScript exports it",
166
+ Detail: pkgPath + "." + symbol.name + " is classified as " + string(entry.Status),
167
+ })
168
+ }
169
+ }
170
+ for symbol := range ledger.Symbols {
171
+ if !goExportSet[symbol] {
172
+ diagnostics = append(diagnostics, Diagnostic{
173
+ Severity: DiagnosticSeverityError,
174
+ Code: "goscript/overrides:parity-unknown-symbol",
175
+ Message: "override parity ledger references a symbol not exported by the Go package",
176
+ Detail: pkgPath + "." + symbol,
177
+ })
178
+ }
179
+ }
180
+ return diagnostics
181
+ }
182
+
183
+ func (symbol goPackageExport) satisfiedBy(export typeScriptExport) bool {
184
+ if symbol.requiresValue {
185
+ return export.value
186
+ }
187
+ return export.typ
188
+ }
189
+
190
+ func (export typeScriptExport) present() bool {
191
+ return export.value || export.typ
192
+ }
193
+
194
+ func exportedPackageSymbols(goPkg *types.Package) []goPackageExport {
195
+ if goPkg == nil || goPkg.Scope() == nil {
196
+ return nil
197
+ }
198
+ names := goPkg.Scope().Names()
199
+ symbols := make([]goPackageExport, 0, len(names))
200
+ for _, name := range names {
201
+ if !ast.IsExported(name) {
202
+ continue
203
+ }
204
+ obj := goPkg.Scope().Lookup(name)
205
+ typeName, isTypeName := obj.(*types.TypeName)
206
+ symbols = append(symbols, goPackageExport{
207
+ name: name,
208
+ requiresValue: !isTypeName || typeNameRequiresValue(typeName),
209
+ })
210
+ }
211
+ slices.SortFunc(symbols, func(a, b goPackageExport) int {
212
+ return strings.Compare(a.name, b.name)
213
+ })
214
+ return symbols
215
+ }
216
+
217
+ func typeNameRequiresValue(typeName *types.TypeName) bool {
218
+ if typeName == nil || typeName.Type() == nil {
219
+ return false
220
+ }
221
+ _, ok := types.Unalias(typeName.Type()).Underlying().(*types.Struct)
222
+ return ok
223
+ }
224
+
225
+ func verifyBlockedOverrideUses(graph *PackageGraph, facts *OverrideFacts) []Diagnostic {
226
+ if graph == nil || facts == nil {
227
+ return nil
228
+ }
229
+ seen := make(map[string]bool)
230
+ var diagnostics []Diagnostic
231
+ for _, node := range graph.Nodes {
232
+ if node == nil || node.OverrideCandidate {
233
+ continue
234
+ }
235
+ pkg := graph.packagesByPath[node.PkgPath]
236
+ if pkg == nil || pkg.TypesInfo == nil {
237
+ continue
238
+ }
239
+ sourcePkgPath := packagePath(pkg)
240
+ for ident, obj := range pkg.TypesInfo.Uses {
241
+ if ident == nil || obj == nil || obj.Pkg() == nil {
242
+ continue
243
+ }
244
+ if sourcePkgPath == obj.Pkg().Path() {
245
+ continue
246
+ }
247
+ ledger := facts.parityLedger(obj.Pkg().Path())
248
+ entry, ok := ledger.Symbols[obj.Name()]
249
+ if !ok || entry.Status != overrideParityStatusBlocked {
250
+ continue
251
+ }
252
+ key := sourcePkgPath + "->" + obj.Pkg().Path() + "." + obj.Name()
253
+ if seen[key] {
254
+ continue
255
+ }
256
+ seen[key] = true
257
+ diagnostics = append(diagnostics, Diagnostic{
258
+ Severity: DiagnosticSeverityError,
259
+ Code: "goscript/overrides:parity-blocked-use",
260
+ Message: "Go code uses an override symbol classified as blocked",
261
+ Detail: obj.Pkg().Path() + "." + obj.Name() + ": " + entry.Reason,
262
+ })
263
+ }
264
+ }
265
+ return diagnostics
266
+ }
267
+
268
+ func (f *OverrideFacts) effectiveTypeScriptExports(pkgPath string) (map[string]typeScriptExport, error) {
269
+ pkg, _, ok := f.copyPackage(pkgPath)
270
+ if !ok {
271
+ return nil, errors.Errorf("override package %q is missing", pkgPath)
272
+ }
273
+ files := make(map[string]string, len(pkg.files))
274
+ for _, file := range pkg.files {
275
+ files[path.Clean(file.path)] = string(file.data)
276
+ }
277
+ exports := make(map[string]typeScriptExport)
278
+ visited := make(map[string]bool)
279
+ if err := collectTypeScriptExports(path.Join(pkgPath, "index.ts"), files, exports, visited); err != nil {
280
+ return nil, err
281
+ }
282
+ return exports, nil
283
+ }
284
+
285
+ func collectTypeScriptExports(
286
+ filePath string,
287
+ files map[string]string,
288
+ exports map[string]typeScriptExport,
289
+ visited map[string]bool,
290
+ ) error {
291
+ filePath = path.Clean(filePath)
292
+ if visited[filePath] {
293
+ return nil
294
+ }
295
+ visited[filePath] = true
296
+ data, ok := files[filePath]
297
+ if !ok {
298
+ return errors.Errorf("missing %s", filePath)
299
+ }
300
+ localBindings := localTypeScriptBindings(data)
301
+ for _, match := range overrideExportDeclarationPattern.FindAllStringSubmatch(data, -1) {
302
+ addTypeScriptExport(exports, match[2], typeScriptExportForDeclaration(match[1]))
303
+ }
304
+ for _, match := range overrideExportNamedPattern.FindAllStringSubmatch(data, -1) {
305
+ namedExports := parseNamedTypeScriptExports(match[2], match[1] != "")
306
+ if match[3] == "" {
307
+ if err := addLocalNamedTypeScriptExports(filePath, exports, localBindings, namedExports); err != nil {
308
+ return err
309
+ }
310
+ continue
311
+ }
312
+ target, ok := resolveOverrideExportPath(filePath, match[3])
313
+ if !ok {
314
+ continue
315
+ }
316
+ targetExports := make(map[string]typeScriptExport)
317
+ if err := collectTypeScriptExports(target, files, targetExports, make(map[string]bool)); err != nil {
318
+ return err
319
+ }
320
+ for _, namedExport := range namedExports {
321
+ sourceExport := targetExports[namedExport.source]
322
+ if !sourceExport.present() {
323
+ return errors.Errorf("%s re-exports missing symbol %s from %s", filePath, namedExport.source, target)
324
+ }
325
+ exportShape := sourceExport
326
+ if namedExport.typeOnly {
327
+ if !sourceExport.typ {
328
+ return errors.Errorf("%s re-exports missing type symbol %s from %s", filePath, namedExport.source, target)
329
+ }
330
+ exportShape = typeScriptExport{typ: true}
331
+ } else if !sourceExport.value {
332
+ return errors.Errorf("%s re-exports missing value symbol %s from %s", filePath, namedExport.source, target)
333
+ }
334
+ addTypeScriptExport(exports, namedExport.target, exportShape)
335
+ }
336
+ }
337
+ for _, match := range overrideExportAllPattern.FindAllStringSubmatch(data, -1) {
338
+ target, ok := resolveOverrideExportPath(filePath, match[1])
339
+ if ok {
340
+ if err := collectTypeScriptExports(target, files, exports, visited); err != nil {
341
+ return err
342
+ }
343
+ }
344
+ }
345
+ return nil
346
+ }
347
+
348
+ func typeScriptExportForDeclaration(kind string) typeScriptExport {
349
+ switch kind {
350
+ case "const", "let", "var", "function":
351
+ return typeScriptExport{value: true}
352
+ case "class", "enum":
353
+ return typeScriptExport{value: true, typ: true}
354
+ case "interface", "type":
355
+ return typeScriptExport{typ: true}
356
+ default:
357
+ return typeScriptExport{}
358
+ }
359
+ }
360
+
361
+ func addTypeScriptExport(exports map[string]typeScriptExport, name string, export typeScriptExport) {
362
+ if name == "" {
363
+ return
364
+ }
365
+ current := exports[name]
366
+ current.value = current.value || export.value
367
+ current.typ = current.typ || export.typ
368
+ exports[name] = current
369
+ }
370
+
371
+ func localTypeScriptBindings(data string) map[string]typeScriptExport {
372
+ bindings := make(map[string]typeScriptExport)
373
+ for _, match := range overrideLocalDeclarationPattern.FindAllStringSubmatch(data, -1) {
374
+ addTypeScriptExport(bindings, match[2], typeScriptExportForDeclaration(match[1]))
375
+ }
376
+ for _, match := range overrideImportNamedPattern.FindAllStringSubmatch(data, -1) {
377
+ for _, namedImport := range parseNamedTypeScriptExports(match[2], match[1] != "") {
378
+ exportShape := typeScriptExport{value: true, typ: true}
379
+ if namedImport.typeOnly {
380
+ exportShape = typeScriptExport{typ: true}
381
+ }
382
+ addTypeScriptExport(bindings, namedImport.target, exportShape)
383
+ }
384
+ }
385
+ return bindings
386
+ }
387
+
388
+ func addLocalNamedTypeScriptExports(
389
+ filePath string,
390
+ exports map[string]typeScriptExport,
391
+ localBindings map[string]typeScriptExport,
392
+ namedExports []namedTypeScriptExport,
393
+ ) error {
394
+ for _, namedExport := range namedExports {
395
+ exportShape := localBindings[namedExport.source]
396
+ if namedExport.typeOnly {
397
+ if !exportShape.typ {
398
+ return errors.Errorf("%s exports missing local type symbol %s", filePath, namedExport.source)
399
+ }
400
+ exportShape = typeScriptExport{typ: true}
401
+ } else if !exportShape.value {
402
+ return errors.Errorf("%s exports missing local value symbol %s", filePath, namedExport.source)
403
+ }
404
+ addTypeScriptExport(exports, namedExport.target, exportShape)
405
+ }
406
+ return nil
407
+ }
408
+
409
+ func parseNamedTypeScriptExports(list string, statementTypeOnly bool) []namedTypeScriptExport {
410
+ var exports []namedTypeScriptExport
411
+ for item := range strings.SplitSeq(list, ",") {
412
+ item = strings.TrimSpace(item)
413
+ if item == "" {
414
+ continue
415
+ }
416
+ typeOnly := statementTypeOnly
417
+ if strings.HasPrefix(item, "type ") {
418
+ typeOnly = true
419
+ item = strings.TrimSpace(strings.TrimPrefix(item, "type "))
420
+ }
421
+ parts := strings.Fields(item)
422
+ if len(parts) == 0 {
423
+ continue
424
+ }
425
+ namedExport := namedTypeScriptExport{
426
+ source: parts[0],
427
+ target: parts[0],
428
+ typeOnly: typeOnly,
429
+ }
430
+ if len(parts) >= 3 && parts[len(parts)-2] == "as" {
431
+ namedExport.target = parts[len(parts)-1]
432
+ }
433
+ exports = append(exports, namedExport)
434
+ }
435
+ return exports
436
+ }
437
+
438
+ func resolveOverrideExportPath(currentFile, spec string) (string, bool) {
439
+ if !strings.HasPrefix(spec, ".") {
440
+ return "", false
441
+ }
442
+ target := path.Clean(path.Join(path.Dir(currentFile), spec))
443
+ switch path.Ext(target) {
444
+ case ".js", ".ts":
445
+ target = strings.TrimSuffix(target, path.Ext(target)) + ".ts"
446
+ case "":
447
+ target = path.Join(target, "index.ts")
448
+ }
449
+ return target, true
450
+ }
@@ -0,0 +1,122 @@
1
+ package compiler
2
+
3
+ import (
4
+ "io"
5
+ "io/fs"
6
+ "maps"
7
+ "path"
8
+ "strings"
9
+
10
+ jsoniter "github.com/aperturerobotics/json-iterator-lite"
11
+ "github.com/pkg/errors"
12
+ )
13
+
14
+ type overrideParityStatus string
15
+
16
+ const (
17
+ overrideParityStatusReal overrideParityStatus = "real"
18
+ overrideParityStatusBlocked overrideParityStatus = "blocked"
19
+ overrideParityStatusDeferred overrideParityStatus = "deferred"
20
+ )
21
+
22
+ type overrideParityLedger struct {
23
+ SchemaVersion int `json:"schemaVersion"`
24
+ Strict bool `json:"strict"`
25
+ Symbols map[string]overrideParityEntry `json:"symbols"`
26
+ }
27
+
28
+ type overrideParityEntry struct {
29
+ Status overrideParityStatus `json:"status"`
30
+ Reason string `json:"reason,omitempty"`
31
+ }
32
+
33
+ func newOverrideParityLedger() overrideParityLedger {
34
+ return overrideParityLedger{
35
+ Symbols: make(map[string]overrideParityEntry),
36
+ }
37
+ }
38
+
39
+ func loadOverrideParityLedger(root overridePackageRoot) (overrideParityLedger, error) {
40
+ ledger := newOverrideParityLedger()
41
+ data, err := fs.ReadFile(root.fsys, path.Join(root.dir, "parity.json"))
42
+ if err != nil {
43
+ if errors.Is(err, fs.ErrNotExist) {
44
+ return ledger, nil
45
+ }
46
+ return overrideParityLedger{}, err
47
+ }
48
+ iter := jsoniter.ParseBytes(data)
49
+ for field := iter.ReadObject(); field != ""; field = iter.ReadObject() {
50
+ switch field {
51
+ case "schemaVersion":
52
+ ledger.SchemaVersion = iter.ReadInt()
53
+ case "strict":
54
+ ledger.Strict = iter.ReadBool()
55
+ case "symbols":
56
+ for symbol := iter.ReadObject(); symbol != ""; symbol = iter.ReadObject() {
57
+ entry := overrideParityEntry{}
58
+ for entryField := iter.ReadObject(); entryField != ""; entryField = iter.ReadObject() {
59
+ switch entryField {
60
+ case "status":
61
+ entry.Status = overrideParityStatus(iter.ReadString())
62
+ case "reason":
63
+ entry.Reason = iter.ReadString()
64
+ default:
65
+ iter.Skip()
66
+ }
67
+ }
68
+ ledger.Symbols[symbol] = entry
69
+ }
70
+ default:
71
+ iter.Skip()
72
+ }
73
+ }
74
+ if iter.Error != nil && !errors.Is(iter.Error, io.EOF) {
75
+ return overrideParityLedger{}, iter.Error
76
+ }
77
+ if ledger.SchemaVersion != 1 {
78
+ return overrideParityLedger{}, errors.New("parity.json schemaVersion must be 1")
79
+ }
80
+ if ledger.Symbols == nil {
81
+ ledger.Symbols = make(map[string]overrideParityEntry)
82
+ }
83
+ for symbol, entry := range ledger.Symbols {
84
+ if symbol == "" {
85
+ return overrideParityLedger{}, errors.New("parity.json symbol names must not be empty")
86
+ }
87
+ if !entry.Status.valid() {
88
+ return overrideParityLedger{}, errors.New("parity.json contains an unknown status")
89
+ }
90
+ if entry.Status == overrideParityStatusBlocked && strings.TrimSpace(entry.Reason) == "" {
91
+ return overrideParityLedger{}, errors.New("parity.json blocked symbols must include a reason")
92
+ }
93
+ }
94
+ return ledger, nil
95
+ }
96
+
97
+ func (s overrideParityStatus) valid() bool {
98
+ switch s {
99
+ case overrideParityStatusReal,
100
+ overrideParityStatusBlocked,
101
+ overrideParityStatusDeferred:
102
+ return true
103
+ default:
104
+ return false
105
+ }
106
+ }
107
+
108
+ func (s overrideParityStatus) requiresExport() bool {
109
+ return s == overrideParityStatusReal
110
+ }
111
+
112
+ func (s overrideParityStatus) forbidsExport() bool {
113
+ return s == overrideParityStatusBlocked
114
+ }
115
+
116
+ func cloneOverrideParityLedger(ledger overrideParityLedger) overrideParityLedger {
117
+ return overrideParityLedger{
118
+ SchemaVersion: ledger.SchemaVersion,
119
+ Strict: ledger.Strict,
120
+ Symbols: maps.Clone(ledger.Symbols),
121
+ }
122
+ }