goscript 0.1.4 → 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 (270) 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} +7 -0
  5. package/cmd/goscript/cmd-test.go +14 -0
  6. package/cmd/goscript/cmd-test_test.go +1 -1
  7. package/compiler/compile-request.go +12 -9
  8. package/compiler/compliance_test.go +0 -1
  9. package/compiler/config.go +2 -0
  10. package/compiler/gotest/request.go +28 -0
  11. package/compiler/gotest/runner.go +353 -27
  12. package/compiler/gotest/runner_test.go +273 -1
  13. package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
  14. package/compiler/gotest/testdata/browserapi/go.mod +3 -0
  15. package/compiler/lowered-program.go +24 -17
  16. package/compiler/lowering.go +392 -127
  17. package/compiler/lowering_bench_test.go +41 -27
  18. package/compiler/override-facts.go +15 -0
  19. package/compiler/override-parity-verifier.go +450 -0
  20. package/compiler/override-parity.go +122 -0
  21. package/compiler/override-registry_test.go +559 -0
  22. package/compiler/protobuf-ts-binding.go +514 -0
  23. package/compiler/protobuf-ts-binding_test.go +172 -0
  24. package/compiler/semantic-model-types.go +9 -4
  25. package/compiler/semantic-model.go +282 -70
  26. package/compiler/semantic-model_test.go +82 -1
  27. package/compiler/service.go +20 -1
  28. package/compiler/skeleton_test.go +62 -8
  29. package/compiler/typescript-emitter.go +128 -13
  30. package/dist/gs/builtin/slice.d.ts +2 -1
  31. package/dist/gs/builtin/slice.js +29 -4
  32. package/dist/gs/builtin/slice.js.map +1 -1
  33. package/dist/gs/builtin/type.d.ts +13 -5
  34. package/dist/gs/builtin/type.js +153 -60
  35. package/dist/gs/builtin/type.js.map +1 -1
  36. package/dist/gs/builtin/varRef.d.ts +11 -0
  37. package/dist/gs/builtin/varRef.js +57 -2
  38. package/dist/gs/builtin/varRef.js.map +1 -1
  39. package/dist/gs/bytes/buffer.gs.js +1 -1
  40. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  41. package/dist/gs/bytes/reader.gs.js +1 -1
  42. package/dist/gs/bytes/reader.gs.js.map +1 -1
  43. package/dist/gs/compress/zlib/index.d.ts +10 -3
  44. package/dist/gs/compress/zlib/index.js +50 -16
  45. package/dist/gs/compress/zlib/index.js.map +1 -1
  46. package/dist/gs/encoding/json/index.d.ts +114 -0
  47. package/dist/gs/encoding/json/index.js +544 -36
  48. package/dist/gs/encoding/json/index.js.map +1 -1
  49. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +100 -0
  50. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +564 -0
  51. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  52. package/dist/gs/github.com/pkg/errors/errors.js +54 -30
  53. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  54. package/dist/gs/go/scanner/index.d.ts +2 -0
  55. package/dist/gs/go/scanner/index.js +29 -5
  56. package/dist/gs/go/scanner/index.js.map +1 -1
  57. package/dist/gs/go/token/index.js +22 -6
  58. package/dist/gs/go/token/index.js.map +1 -1
  59. package/dist/gs/hash/index.d.ts +6 -0
  60. package/dist/gs/hash/index.js +20 -0
  61. package/dist/gs/hash/index.js.map +1 -1
  62. package/dist/gs/internal/goarch/index.d.ts +43 -3
  63. package/dist/gs/internal/goarch/index.js +42 -10
  64. package/dist/gs/internal/goarch/index.js.map +1 -1
  65. package/dist/gs/io/fs/fs.js +26 -14
  66. package/dist/gs/io/fs/fs.js.map +1 -1
  67. package/dist/gs/io/fs/readdir.js +4 -2
  68. package/dist/gs/io/fs/readdir.js.map +1 -1
  69. package/dist/gs/io/fs/sub.js +8 -1
  70. package/dist/gs/io/fs/sub.js.map +1 -1
  71. package/dist/gs/io/io.d.ts +2 -0
  72. package/dist/gs/io/io.js.map +1 -1
  73. package/dist/gs/math/bits/index.d.ts +5 -0
  74. package/dist/gs/math/bits/index.js +16 -4
  75. package/dist/gs/math/bits/index.js.map +1 -1
  76. package/dist/gs/mime/index.d.ts +16 -0
  77. package/dist/gs/mime/index.js +315 -6
  78. package/dist/gs/mime/index.js.map +1 -1
  79. package/dist/gs/net/http/httptest/index.d.ts +12 -0
  80. package/dist/gs/net/http/httptest/index.js +85 -6
  81. package/dist/gs/net/http/httptest/index.js.map +1 -1
  82. package/dist/gs/net/http/index.d.ts +300 -5
  83. package/dist/gs/net/http/index.js +1598 -58
  84. package/dist/gs/net/http/index.js.map +1 -1
  85. package/dist/gs/os/dir_unix.gs.js +1 -1
  86. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  87. package/dist/gs/os/error.gs.js +1 -1
  88. package/dist/gs/os/error.gs.js.map +1 -1
  89. package/dist/gs/os/exec.gs.d.ts +1 -0
  90. package/dist/gs/os/exec.gs.js +4 -8
  91. package/dist/gs/os/exec.gs.js.map +1 -1
  92. package/dist/gs/os/exec_posix.gs.js +1 -1
  93. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  94. package/dist/gs/os/index.d.ts +1 -1
  95. package/dist/gs/os/index.js +1 -1
  96. package/dist/gs/os/index.js.map +1 -1
  97. package/dist/gs/os/proc.gs.d.ts +4 -0
  98. package/dist/gs/os/proc.gs.js +12 -6
  99. package/dist/gs/os/proc.gs.js.map +1 -1
  100. package/dist/gs/os/root_js.gs.js +1 -1
  101. package/dist/gs/os/root_js.gs.js.map +1 -1
  102. package/dist/gs/os/types.gs.js +1 -1
  103. package/dist/gs/os/types.gs.js.map +1 -1
  104. package/dist/gs/os/types_js.gs.js +1 -1
  105. package/dist/gs/os/types_js.gs.js.map +1 -1
  106. package/dist/gs/os/types_unix.gs.js +1 -1
  107. package/dist/gs/os/types_unix.gs.js.map +1 -1
  108. package/dist/gs/path/path.js +11 -7
  109. package/dist/gs/path/path.js.map +1 -1
  110. package/dist/gs/reflect/index.d.ts +5 -4
  111. package/dist/gs/reflect/index.js +4 -3
  112. package/dist/gs/reflect/index.js.map +1 -1
  113. package/dist/gs/reflect/map.js +15 -0
  114. package/dist/gs/reflect/map.js.map +1 -1
  115. package/dist/gs/reflect/type.d.ts +25 -6
  116. package/dist/gs/reflect/type.js +1418 -228
  117. package/dist/gs/reflect/type.js.map +1 -1
  118. package/dist/gs/reflect/types.d.ts +14 -6
  119. package/dist/gs/reflect/types.js +35 -1
  120. package/dist/gs/reflect/types.js.map +1 -1
  121. package/dist/gs/reflect/value.d.ts +1 -0
  122. package/dist/gs/reflect/value.js +83 -41
  123. package/dist/gs/reflect/value.js.map +1 -1
  124. package/dist/gs/reflect/visiblefields.js +4 -140
  125. package/dist/gs/reflect/visiblefields.js.map +1 -1
  126. package/dist/gs/runtime/pprof/index.d.ts +8 -2
  127. package/dist/gs/runtime/pprof/index.js +50 -30
  128. package/dist/gs/runtime/pprof/index.js.map +1 -1
  129. package/dist/gs/runtime/runtime.js +5 -4
  130. package/dist/gs/runtime/runtime.js.map +1 -1
  131. package/dist/gs/runtime/trace/index.js +5 -19
  132. package/dist/gs/runtime/trace/index.js.map +1 -1
  133. package/dist/gs/strconv/atoi.gs.js +1 -1
  134. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  135. package/dist/gs/strconv/complex.gs.d.ts +3 -0
  136. package/dist/gs/strconv/complex.gs.js +148 -0
  137. package/dist/gs/strconv/complex.gs.js.map +1 -0
  138. package/dist/gs/strconv/index.d.ts +1 -0
  139. package/dist/gs/strconv/index.js +1 -0
  140. package/dist/gs/strconv/index.js.map +1 -1
  141. package/dist/gs/strings/builder.js +1 -1
  142. package/dist/gs/strings/reader.js +9 -5
  143. package/dist/gs/strings/reader.js.map +1 -1
  144. package/dist/gs/strings/replace.js +15 -7
  145. package/dist/gs/strings/replace.js.map +1 -1
  146. package/dist/gs/strings/strings.d.ts +5 -0
  147. package/dist/gs/strings/strings.js +57 -5
  148. package/dist/gs/strings/strings.js.map +1 -1
  149. package/dist/gs/sync/atomic/type.gs.js +9 -9
  150. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  151. package/dist/gs/sync/atomic/value.gs.js +2 -2
  152. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  153. package/dist/gs/syscall/env.js +22 -14
  154. package/dist/gs/syscall/env.js.map +1 -1
  155. package/dist/gs/testing/testing.js +55 -13
  156. package/dist/gs/testing/testing.js.map +1 -1
  157. package/dist/gs/time/time.d.ts +24 -1
  158. package/dist/gs/time/time.js +43 -3
  159. package/dist/gs/time/time.js.map +1 -1
  160. package/dist/gs/unique/index.js +7 -1
  161. package/dist/gs/unique/index.js.map +1 -1
  162. package/go.mod +3 -3
  163. package/go.sum +16 -0
  164. package/gs/builtin/runtime-contract.test.ts +218 -21
  165. package/gs/builtin/slice.ts +44 -4
  166. package/gs/builtin/type.ts +226 -59
  167. package/gs/builtin/varRef.ts +85 -2
  168. package/gs/bytes/buffer.gs.ts +1 -1
  169. package/gs/bytes/reader.gs.ts +1 -1
  170. package/gs/compress/zlib/index.test.ts +62 -1
  171. package/gs/compress/zlib/index.ts +53 -16
  172. package/gs/compress/zlib/parity.json +51 -0
  173. package/gs/encoding/json/index.test.ts +360 -6
  174. package/gs/encoding/json/index.ts +679 -38
  175. package/gs/encoding/json/parity.json +81 -0
  176. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +211 -3
  177. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +857 -1
  178. package/gs/github.com/pkg/errors/errors.ts +54 -30
  179. package/gs/go/scanner/index.test.ts +39 -56
  180. package/gs/go/scanner/index.ts +33 -5
  181. package/gs/go/scanner/parity.json +27 -0
  182. package/gs/go/token/index.ts +22 -6
  183. package/gs/hash/index.test.ts +20 -33
  184. package/gs/hash/index.ts +28 -0
  185. package/gs/hash/parity.json +21 -0
  186. package/gs/internal/goarch/index.test.ts +32 -0
  187. package/gs/internal/goarch/index.ts +45 -13
  188. package/gs/internal/goarch/parity.json +144 -0
  189. package/gs/io/fs/fs.ts +26 -14
  190. package/gs/io/fs/readdir.ts +4 -4
  191. package/gs/io/fs/sub.ts +8 -1
  192. package/gs/io/io.ts +1 -0
  193. package/gs/io/parity.json +162 -0
  194. package/gs/math/bits/index.test.ts +14 -1
  195. package/gs/math/bits/index.ts +23 -4
  196. package/gs/math/bits/parity.json +156 -0
  197. package/gs/mime/index.test.ts +90 -0
  198. package/gs/mime/index.ts +369 -6
  199. package/gs/mime/parity.json +36 -0
  200. package/gs/net/http/httptest/index.test.ts +98 -2
  201. package/gs/net/http/httptest/index.ts +101 -6
  202. package/gs/net/http/httptest/parity.json +15 -0
  203. package/gs/net/http/index.test.ts +781 -12
  204. package/gs/net/http/index.ts +1860 -139
  205. package/gs/net/http/meta.json +16 -1
  206. package/gs/net/http/parity.json +193 -0
  207. package/gs/os/dir_unix.gs.ts +1 -1
  208. package/gs/os/error.gs.ts +1 -1
  209. package/gs/os/exec.gs.ts +4 -8
  210. package/gs/os/exec_posix.gs.ts +1 -1
  211. package/gs/os/index.test.ts +9 -0
  212. package/gs/os/index.ts +1 -0
  213. package/gs/os/parity.json +9 -0
  214. package/gs/os/proc.gs.ts +18 -5
  215. package/gs/os/proc.test.ts +26 -0
  216. package/gs/os/root_js.gs.ts +1 -1
  217. package/gs/os/types.gs.ts +1 -1
  218. package/gs/os/types_js.gs.ts +1 -1
  219. package/gs/os/types_unix.gs.ts +1 -1
  220. package/gs/path/path.ts +11 -7
  221. package/gs/reflect/field.test.ts +37 -15
  222. package/gs/reflect/function-types.test.ts +518 -22
  223. package/gs/reflect/index.ts +8 -6
  224. package/gs/reflect/map.ts +20 -0
  225. package/gs/reflect/meta.json +6 -4
  226. package/gs/reflect/parity.json +234 -0
  227. package/gs/reflect/sliceat.test.ts +156 -0
  228. package/gs/reflect/structof.test.ts +401 -0
  229. package/gs/reflect/type.ts +1897 -317
  230. package/gs/reflect/typefor.test.ts +510 -10
  231. package/gs/reflect/types.ts +43 -18
  232. package/gs/reflect/value.ts +105 -45
  233. package/gs/reflect/visiblefields.ts +5 -168
  234. package/gs/runtime/parity.json +24 -0
  235. package/gs/runtime/pprof/index.test.ts +29 -7
  236. package/gs/runtime/pprof/index.ts +56 -30
  237. package/gs/runtime/pprof/parity.json +27 -0
  238. package/gs/runtime/runtime.test.ts +3 -1
  239. package/gs/runtime/runtime.ts +4 -3
  240. package/gs/runtime/trace/index.test.ts +5 -3
  241. package/gs/runtime/trace/index.ts +8 -20
  242. package/gs/runtime/trace/parity.json +36 -0
  243. package/gs/strconv/atoi.gs.ts +1 -1
  244. package/gs/strconv/complex.gs.ts +174 -0
  245. package/gs/strconv/complex.test.ts +65 -0
  246. package/gs/strconv/index.ts +1 -0
  247. package/gs/strconv/parity.json +120 -0
  248. package/gs/strings/builder.ts +1 -1
  249. package/gs/strings/parity.json +186 -0
  250. package/gs/strings/reader.ts +9 -5
  251. package/gs/strings/replace.ts +15 -7
  252. package/gs/strings/strings.test.ts +22 -2
  253. package/gs/strings/strings.ts +64 -6
  254. package/gs/sync/atomic/type.gs.ts +9 -9
  255. package/gs/sync/atomic/value.gs.ts +2 -2
  256. package/gs/syscall/env.ts +29 -14
  257. package/gs/testing/testing.test.ts +67 -0
  258. package/gs/testing/testing.ts +87 -19
  259. package/gs/time/parity.json +225 -0
  260. package/gs/time/time.test.ts +20 -2
  261. package/gs/time/time.ts +49 -7
  262. package/gs/unique/index.ts +7 -1
  263. package/package.json +4 -2
  264. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
  265. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -926
  266. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
  267. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -38
  268. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1361
  269. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
  270. /package/compiler/{wasm_api.go → wasm-api.go} +0 -0
@@ -0,0 +1,514 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "os"
7
+ "path/filepath"
8
+ "strings"
9
+ )
10
+
11
+ type protobufTypeScriptBinding struct {
12
+ sourcePath string
13
+ outputName string
14
+ importSource string
15
+ messageNames map[string]string
16
+ hasOneof bool
17
+ }
18
+
19
+ func protobufTypeScriptBindings(semPkg *semanticPackage, options LoweringOptions) (map[string]protobufTypeScriptBinding, []Diagnostic) {
20
+ if semPkg == nil || semPkg.source == nil || !options.ProtobufTypeScriptBinding {
21
+ return nil, nil
22
+ }
23
+ bindings := make(map[string]protobufTypeScriptBinding)
24
+ var diagnostics []Diagnostic
25
+ for idx, syntax := range semPkg.source.Syntax {
26
+ sourcePath := sourceFilePath(semPkg, idx, syntax)
27
+ if !strings.HasSuffix(sourcePath, ".pb.go") {
28
+ continue
29
+ }
30
+ if strings.HasSuffix(filepath.Base(sourcePath), "_srpc.pb.go") {
31
+ continue
32
+ }
33
+ if !protobufTypeScriptBindingInSourceRoot(options.SourceRoot, sourcePath) {
34
+ continue
35
+ }
36
+ tsPath := strings.TrimSuffix(sourcePath, ".go") + ".ts"
37
+ if _, err := os.Stat(tsPath); err != nil {
38
+ if os.IsNotExist(err) {
39
+ diagnostics = append(diagnostics, Diagnostic{
40
+ Severity: DiagnosticSeverityError,
41
+ Code: "goscript/protobuf-ts-binding:missing",
42
+ Message: "protobuf TypeScript binding is missing sibling .pb.ts",
43
+ Detail: fmt.Sprintf("%s requires %s", sourcePath, tsPath),
44
+ })
45
+ continue
46
+ }
47
+ diagnostics = append(diagnostics, Diagnostic{
48
+ Severity: DiagnosticSeverityError,
49
+ Code: "goscript/protobuf-ts-binding:stat",
50
+ Message: "failed to inspect protobuf TypeScript binding",
51
+ Detail: err.Error(),
52
+ })
53
+ continue
54
+ }
55
+ importSource, err := protobufTypeScriptBindingImportSource(options.OutputPath, semPkg.pkgPath, tsPath)
56
+ if err != nil {
57
+ diagnostics = append(diagnostics, Diagnostic{
58
+ Severity: DiagnosticSeverityError,
59
+ Code: "goscript/protobuf-ts-binding:import-source",
60
+ Message: "failed to compute protobuf TypeScript binding import",
61
+ Detail: err.Error(),
62
+ })
63
+ continue
64
+ }
65
+ bindings[sourcePath] = protobufTypeScriptBinding{
66
+ sourcePath: sourcePath,
67
+ outputName: strings.TrimSuffix(filepath.Base(sourcePath), ".go") + ".ts",
68
+ importSource: importSource,
69
+ messageNames: protobufTypeScriptBindingMessageNames(syntax),
70
+ hasOneof: protobufTypeScriptBindingHasOneof(syntax),
71
+ }
72
+ }
73
+ return bindings, diagnostics
74
+ }
75
+
76
+ func protobufTypeScriptBindingRoot(dir string) string {
77
+ dir = strings.TrimSpace(dir)
78
+ if dir == "" {
79
+ return ""
80
+ }
81
+ abs, err := filepath.Abs(dir)
82
+ if err != nil {
83
+ return dir
84
+ }
85
+ for {
86
+ if _, err := os.Stat(filepath.Join(abs, "go.mod")); err == nil {
87
+ return abs
88
+ }
89
+ parent := filepath.Dir(abs)
90
+ if parent == abs {
91
+ return dir
92
+ }
93
+ abs = parent
94
+ }
95
+ }
96
+
97
+ func protobufTypeScriptBindingInSourceRoot(sourceRoot, sourcePath string) bool {
98
+ if strings.TrimSpace(sourceRoot) == "" {
99
+ return true
100
+ }
101
+ rootAbs, err := filepath.Abs(sourceRoot)
102
+ if err != nil {
103
+ return false
104
+ }
105
+ sourceAbs, err := filepath.Abs(sourcePath)
106
+ if err != nil {
107
+ return false
108
+ }
109
+ rel, err := filepath.Rel(rootAbs, sourceAbs)
110
+ if err != nil {
111
+ return false
112
+ }
113
+ return rel == "." || !strings.HasPrefix(rel, ".."+string(filepath.Separator)) && rel != ".." && !filepath.IsAbs(rel)
114
+ }
115
+
116
+ func protobufTypeScriptBindingImportSource(outputPath, pkgPath, tsPath string) (string, error) {
117
+ outputDir := filepath.Join(outputPath, "@goscript", filepath.FromSlash(pkgPath))
118
+ outputDir, err := filepath.Abs(outputDir)
119
+ if err != nil {
120
+ return "", err
121
+ }
122
+ rel, err := filepath.Rel(outputDir, tsPath)
123
+ if err != nil {
124
+ return "", err
125
+ }
126
+ rel = filepath.ToSlash(rel)
127
+ if !strings.HasPrefix(rel, ".") {
128
+ rel = "./" + rel
129
+ }
130
+ return strings.TrimSuffix(rel, ".ts") + ".js", nil
131
+ }
132
+
133
+ func protobufTypeScriptBindingHasOneof(file *ast.File) bool {
134
+ if file == nil {
135
+ return false
136
+ }
137
+ for _, decl := range file.Decls {
138
+ genDecl, ok := decl.(*ast.GenDecl)
139
+ if !ok {
140
+ continue
141
+ }
142
+ for _, spec := range genDecl.Specs {
143
+ typeSpec, ok := spec.(*ast.TypeSpec)
144
+ if !ok {
145
+ continue
146
+ }
147
+ structType, ok := typeSpec.Type.(*ast.StructType)
148
+ if !ok || structType.Fields == nil {
149
+ continue
150
+ }
151
+ for _, field := range structType.Fields.List {
152
+ if field.Tag != nil && strings.Contains(field.Tag.Value, "protobuf_oneof") {
153
+ return true
154
+ }
155
+ }
156
+ }
157
+ }
158
+ return false
159
+ }
160
+
161
+ func protobufTypeScriptBindingMessageNames(file *ast.File) map[string]string {
162
+ names := make(map[string]string)
163
+ if file == nil {
164
+ return names
165
+ }
166
+ for _, decl := range file.Decls {
167
+ genDecl, ok := decl.(*ast.GenDecl)
168
+ if !ok {
169
+ continue
170
+ }
171
+ for _, spec := range genDecl.Specs {
172
+ typeSpec, ok := spec.(*ast.TypeSpec)
173
+ if !ok {
174
+ continue
175
+ }
176
+ if _, ok := typeSpec.Type.(*ast.StructType); !ok {
177
+ continue
178
+ }
179
+ name := typeSpec.Name.Name
180
+ names[name] = protobufTypeScriptBindingSafeIdentifier(name)
181
+ }
182
+ }
183
+ return names
184
+ }
185
+
186
+ func protobufTypeScriptBindingSafeIdentifier(name string) string {
187
+ switch name {
188
+ case "break",
189
+ "case",
190
+ "catch",
191
+ "class",
192
+ "const",
193
+ "continue",
194
+ "debugger",
195
+ "default",
196
+ "delete",
197
+ "do",
198
+ "else",
199
+ "export",
200
+ "extends",
201
+ "false",
202
+ "finally",
203
+ "for",
204
+ "function",
205
+ "if",
206
+ "import",
207
+ "in",
208
+ "instanceof",
209
+ "new",
210
+ "null",
211
+ "return",
212
+ "super",
213
+ "switch",
214
+ "this",
215
+ "throw",
216
+ "true",
217
+ "try",
218
+ "typeof",
219
+ "var",
220
+ "void",
221
+ "while",
222
+ "with",
223
+ "yield",
224
+ "enum",
225
+ "implements",
226
+ "interface",
227
+ "let",
228
+ "package",
229
+ "private",
230
+ "protected",
231
+ "public",
232
+ "static",
233
+ "Object",
234
+ "bigint",
235
+ "number",
236
+ "boolean",
237
+ "string",
238
+ "object",
239
+ "globalThis",
240
+ "Uint8Array",
241
+ "Partial":
242
+ return name + "$"
243
+ default:
244
+ return name
245
+ }
246
+ }
247
+
248
+ func rewriteProtobufTypeScriptBindingFile(file *loweredFile, binding protobufTypeScriptBinding) {
249
+ if file == nil {
250
+ return
251
+ }
252
+ file.outputName = binding.outputName
253
+ if binding.hasOneof {
254
+ return
255
+ }
256
+ const importAlias = "__protobuf_ts"
257
+ file.imports = append(file.imports, loweredImport{
258
+ alias: importAlias,
259
+ source: binding.importSource,
260
+ sideEffect: true,
261
+ })
262
+ var setupDecls []loweredDecl
263
+ for _, decl := range file.decls {
264
+ if decl.structType == nil {
265
+ continue
266
+ }
267
+ if protobufTypeScriptBindingSyntheticMapEntry(decl.structType.name) {
268
+ continue
269
+ }
270
+ rewriteProtobufTypeScriptBindingStruct(decl.structType)
271
+ setup := protobufTypeScriptBindingStructSetupDecl(decl.structType, importAlias, binding.messageNames[decl.structType.name])
272
+ if setup.code != "" {
273
+ setupDecls = append(setupDecls, setup)
274
+ }
275
+ }
276
+ file.decls = append(file.decls, setupDecls...)
277
+ }
278
+
279
+ func protobufTypeScriptBindingSyntheticMapEntry(name string) bool {
280
+ return strings.Contains(name, "_") && strings.HasSuffix(name, "Entry")
281
+ }
282
+
283
+ func lowerProtobufSRPCTypeScriptBindingStub(semPkg *semanticPackage, sourcePath string, options LoweringOptions) (*loweredFile, []Diagnostic) {
284
+ tsPath := strings.TrimSuffix(sourcePath, ".go") + ".ts"
285
+ if _, err := os.Stat(tsPath); err != nil {
286
+ if os.IsNotExist(err) {
287
+ return nil, nil
288
+ }
289
+ return nil, []Diagnostic{{
290
+ Severity: DiagnosticSeverityError,
291
+ Code: "goscript/protobuf-ts-binding:srpc-stat",
292
+ Message: "failed to inspect SRPC TypeScript binding",
293
+ Detail: err.Error(),
294
+ }}
295
+ }
296
+ importSource, err := protobufTypeScriptBindingImportSource(options.OutputPath, semPkg.pkgPath, tsPath)
297
+ if err != nil {
298
+ return nil, []Diagnostic{{
299
+ Severity: DiagnosticSeverityError,
300
+ Code: "goscript/protobuf-ts-binding:srpc-import-source",
301
+ Message: "failed to compute SRPC TypeScript binding import",
302
+ Detail: err.Error(),
303
+ }}
304
+ }
305
+ return &loweredFile{
306
+ sourcePath: sourcePath,
307
+ outputName: sourceOutputName(sourcePath),
308
+ decls: []loweredDecl{{
309
+ code: "export * from " + strconvQuote(importSource),
310
+ }},
311
+ exportAll: true,
312
+ }, nil
313
+ }
314
+
315
+ func protobufSRPCHasGoScriptReplacement(sourcePath string) bool {
316
+ base := filepath.Base(sourcePath)
317
+ if !strings.HasSuffix(base, "_srpc.pb.go") {
318
+ return false
319
+ }
320
+ replacement := strings.TrimSuffix(base, "_srpc.pb.go") + "-srpc-goscript.go"
321
+ _, err := os.Stat(filepath.Join(filepath.Dir(sourcePath), replacement))
322
+ return err == nil
323
+ }
324
+
325
+ func rewriteProtobufTypeScriptBindingStruct(structType *loweredStruct) {
326
+ if structType == nil {
327
+ return
328
+ }
329
+ for idx := range structType.methods {
330
+ method := &structType.methods[idx]
331
+ body := protobufTypeScriptBindingMethodBody(structType, method)
332
+ if body == "" {
333
+ continue
334
+ }
335
+ method.async = false
336
+ method.paramBindings = nil
337
+ method.namedResults = nil
338
+ method.deferState = nil
339
+ method.body = []loweredStmt{{text: body}}
340
+ }
341
+ }
342
+
343
+ func protobufTypeScriptBindingMethodBody(structType *loweredStruct, method *loweredFunction) string {
344
+ if structType == nil || method == nil {
345
+ return ""
346
+ }
347
+ ctor := structType.name
348
+ if !protobufTypeScriptBindingReplacesMethodName(method.name) {
349
+ return ""
350
+ }
351
+ switch method.name {
352
+ case "CloneMessageVT":
353
+ return "return $.interfaceValue<protobuf_go_lite.CloneMessage | null>(protobuf_go_lite.CloneBoundMessage(" +
354
+ ctor + ", this) as any, " + strconvQuote("*"+structType.typeName) + ")"
355
+ case "CloneVT":
356
+ return "return protobuf_go_lite.CloneBoundMessage(" + ctor + ", this) as any"
357
+ case "EqualVT":
358
+ return "return protobuf_go_lite.EqualBoundMessage(" + ctor + ", this, " + protobufBindingParam(method, 0, "null") + ")"
359
+ case "MarshalJSON":
360
+ return "return protobuf_go_lite.MarshalBoundMessageJSON(" + ctor + ", this)"
361
+ case "MarshalProtoJSON":
362
+ return "protobuf_go_lite.MarshalBoundMessageProtoJSON(" + ctor + ", this, " + protobufBindingParam(method, 0, "null") + ")"
363
+ case "MarshalProtoText", "String":
364
+ return "return protobuf_go_lite.MarshalBoundMessageProtoText(" + ctor + ", this)"
365
+ case "MarshalToSizedBufferVT":
366
+ return "return protobuf_go_lite.MarshalBoundMessageToSizedBufferVT(" + ctor + ", this, " + protobufBindingParam(method, 0, "null") + ")"
367
+ case "MarshalVT":
368
+ return "return protobuf_go_lite.MarshalBoundMessageVT(" + ctor + ", this)"
369
+ case "ProtoMessage":
370
+ return "return"
371
+ case "Reset":
372
+ return "$.assignStruct($.pointerValue<" + ctor + ">(this), $.markAsStructValue(new " + ctor + "()))"
373
+ case "SizeVT":
374
+ return "return protobuf_go_lite.SizeBoundMessageVT(" + ctor + ", this)"
375
+ case "UnmarshalJSON":
376
+ return "return protobuf_go_lite.UnmarshalBoundMessageJSON(" + ctor + ", this, " + protobufBindingParam(method, 0, "null") + ")"
377
+ case "UnmarshalProtoJSON":
378
+ return "protobuf_go_lite.UnmarshalBoundMessageProtoJSON(" + ctor + ", this, " + protobufBindingParam(method, 0, "null") + ")"
379
+ case "UnmarshalVT":
380
+ return "return protobuf_go_lite.UnmarshalBoundMessageVT(" + ctor + ", this, " + protobufBindingParam(method, 0, "null") + ")"
381
+ default:
382
+ return ""
383
+ }
384
+ }
385
+
386
+ func protobufTypeScriptBindingReplacesMethodName(name string) bool {
387
+ switch name {
388
+ case "CloneMessageVT",
389
+ "CloneVT",
390
+ "EqualVT",
391
+ "MarshalJSON",
392
+ "MarshalProtoJSON",
393
+ "MarshalProtoText",
394
+ "MarshalToSizedBufferVT",
395
+ "MarshalVT",
396
+ "ProtoMessage",
397
+ "Reset",
398
+ "SizeVT",
399
+ "String",
400
+ "UnmarshalJSON",
401
+ "UnmarshalProtoJSON",
402
+ "UnmarshalVT":
403
+ return true
404
+ default:
405
+ return false
406
+ }
407
+ }
408
+
409
+ func protobufBindingParam(method *loweredFunction, idx int, fallback string) string {
410
+ if method == nil || idx < 0 || idx >= len(method.params) || strings.TrimSpace(method.params[idx].name) == "" {
411
+ return fallback
412
+ }
413
+ return method.params[idx].name
414
+ }
415
+
416
+ func protobufTypeScriptBindingStructSetupDecl(structType *loweredStruct, importAlias, messageName string) loweredDecl {
417
+ if structType == nil {
418
+ return loweredDecl{}
419
+ }
420
+ if messageName == "" {
421
+ messageName = structType.name
422
+ }
423
+ entries := make([]string, 0, len(structType.fields))
424
+ for _, field := range structType.fields {
425
+ ctor := protobufTypeScriptBindingFieldCtor(field)
426
+ if ctor == "" {
427
+ continue
428
+ }
429
+ entries = append(entries, strconvQuote(protobufTypeScriptBindingFieldLocalName(field))+": "+ctor)
430
+ }
431
+ return loweredDecl{code: "(" + structType.name + " as any).__protobufTypeScriptMessage = " + importAlias + "." + messageName + ";\n" +
432
+ "(" + structType.name + " as any).__protobufTypeScriptFields = {" + strings.Join(entries, ", ") + "};"}
433
+ }
434
+
435
+ func protobufTypeScriptBindingFieldLocalName(field loweredStructField) string {
436
+ if tag := field.tag; tag != "" {
437
+ if value := protobufTypeScriptBindingTagValue(tag, "json="); value != "" {
438
+ return value
439
+ }
440
+ if value := protobufTypeScriptBindingTagValue(tag, "name="); value != "" {
441
+ return protobufTypeScriptBindingProtoCamel(value)
442
+ }
443
+ }
444
+ if field.name == "" {
445
+ return field.name
446
+ }
447
+ return strings.ToLower(field.name[:1]) + field.name[1:]
448
+ }
449
+
450
+ func protobufTypeScriptBindingTagValue(tag, key string) string {
451
+ idx := strings.Index(tag, key)
452
+ if idx < 0 {
453
+ return ""
454
+ }
455
+ rest := tag[idx+len(key):]
456
+ end := len(rest)
457
+ for idx, ch := range rest {
458
+ if ch == ',' || ch == '"' || ch == '`' || ch == ' ' {
459
+ end = idx
460
+ break
461
+ }
462
+ }
463
+ return rest[:end]
464
+ }
465
+
466
+ func protobufTypeScriptBindingProtoCamel(name string) string {
467
+ if name == "" {
468
+ return ""
469
+ }
470
+ parts := strings.Split(name, "_")
471
+ var out strings.Builder
472
+ for idx, part := range parts {
473
+ if part == "" {
474
+ continue
475
+ }
476
+ part = strings.ToLower(part)
477
+ if idx == 0 {
478
+ out.WriteString(part)
479
+ continue
480
+ }
481
+ out.WriteString(strings.ToUpper(part[:1]))
482
+ out.WriteString(part[1:])
483
+ }
484
+ return out.String()
485
+ }
486
+
487
+ func protobufTypeScriptBindingFieldCtor(field loweredStructField) string {
488
+ if !strings.Contains(field.runtimeType, "TypeKind.Pointer") {
489
+ return ""
490
+ }
491
+ for _, token := range strings.FieldsFunc(field.typ, func(r rune) bool {
492
+ return !(r == '.' || r == '_' || r == '$' || r >= '0' && r <= '9' || r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z')
493
+ }) {
494
+ if protobufTypeScriptBindingCtorToken(token) {
495
+ return token
496
+ }
497
+ }
498
+ return ""
499
+ }
500
+
501
+ func protobufTypeScriptBindingCtorToken(token string) bool {
502
+ if token == "" || strings.HasPrefix(token, "$.") || strings.HasPrefix(token, "globalThis.") {
503
+ return false
504
+ }
505
+ switch token {
506
+ case "any", "bigint", "boolean", "Date", "Map", "null", "number", "Promise", "Set", "Slice", "string", "Uint8Array", "undefined", "unknown", "VarRef", "void":
507
+ return false
508
+ }
509
+ if strings.Contains(token, ".") {
510
+ return true
511
+ }
512
+ first := token[0]
513
+ return first >= 'A' && first <= 'Z'
514
+ }
@@ -0,0 +1,172 @@
1
+ package compiler
2
+
3
+ import (
4
+ "context"
5
+ "errors"
6
+ "go/ast"
7
+ "os"
8
+ "path/filepath"
9
+ "strings"
10
+ "testing"
11
+
12
+ "golang.org/x/tools/go/packages"
13
+ )
14
+
15
+ func TestProtobufTypeScriptBindingSkipsPbGoEmission(t *testing.T) {
16
+ dir := t.TempDir()
17
+ writeTestFile(t, dir, "go.mod", "module example.test/protobufbinding\n\ngo 1.25\n")
18
+ writeTestFile(t, dir, "foo.pb.go", `package protobufbinding
19
+
20
+ type Foo struct {
21
+ Name string
22
+ }
23
+
24
+ type Object struct {
25
+ Name string
26
+ }
27
+ `)
28
+ writeTestFile(t, dir, "foo.pb.ts", `export interface Foo {
29
+ name?: string
30
+ }
31
+ export const Foo = {} as any
32
+ export interface Object$ {
33
+ name?: string
34
+ }
35
+ export const Object$ = {} as any
36
+ `)
37
+ writeTestFile(t, dir, "use.go", `package protobufbinding
38
+
39
+ func NewFoo() Foo {
40
+ return Foo{Name: "bound"}
41
+ }
42
+ `)
43
+
44
+ out := filepath.Join(dir, "out")
45
+ comp, err := NewCompiler(&Config{
46
+ Dir: dir,
47
+ OutputPath: out,
48
+ ProtobufTypeScriptBinding: true,
49
+ }, nil, nil)
50
+ if err != nil {
51
+ t.Fatal(err)
52
+ }
53
+ if _, err := comp.CompilePackages(context.Background(), "."); err != nil {
54
+ t.Fatalf("compile with protobuf TypeScript binding: %v", err)
55
+ }
56
+
57
+ pkgDir := filepath.Join(out, "@goscript", "example.test", "protobufbinding")
58
+ if _, err := os.Stat(filepath.Join(pkgDir, "foo.pb.gs.ts")); !errors.Is(err, os.ErrNotExist) {
59
+ t.Fatalf("bound protobuf file should not emit foo.pb.gs.ts, stat err=%v", err)
60
+ }
61
+ binding := readTestFile(t, filepath.Join(pkgDir, "foo.pb.ts"))
62
+ if !strings.Contains(binding, `import * as __protobuf_ts`) || !strings.Contains(binding, `foo.pb.js`) ||
63
+ !strings.Contains(binding, `class Foo`) || !strings.Contains(binding, `__protobufTypeScriptMessage = __protobuf_ts.Foo`) {
64
+ t.Fatalf("binding file should adapt sibling foo.pb.js, got:\n%s", binding)
65
+ }
66
+ if !strings.Contains(binding, `class Object`) || !strings.Contains(binding, `__protobufTypeScriptMessage = __protobuf_ts.Object$`) {
67
+ t.Fatalf("binding file should use protobuf-es-lite safe identifier for Object, got:\n%s", binding)
68
+ }
69
+ if !strings.Contains(binding, `__protobufTypeScriptMessage = __protobuf_ts.Foo;`) ||
70
+ !strings.Contains(binding, `__protobufTypeScriptFields = {};`) {
71
+ t.Fatalf("binding metadata assignments should be semicolon-terminated to avoid ASI calls, got:\n%s", binding)
72
+ }
73
+ index := readTestFile(t, filepath.Join(pkgDir, "index.ts"))
74
+ if !strings.Contains(index, `export { Foo, Object } from "./foo.pb.ts"`) {
75
+ t.Fatalf("package index should re-export binding file, got:\n%s", index)
76
+ }
77
+ use := readTestFile(t, filepath.Join(pkgDir, "use.gs.ts"))
78
+ if !strings.Contains(use, `from "./foo.pb.ts"`) {
79
+ t.Fatalf("non-protobuf file should import bound protobuf declarations, got:\n%s", use)
80
+ }
81
+ }
82
+
83
+ func TestProtobufTypeScriptBindingReportsMissingSibling(t *testing.T) {
84
+ dir := t.TempDir()
85
+ writeTestFile(t, dir, "go.mod", "module example.test/missingpbts\n\ngo 1.25\n")
86
+ writeTestFile(t, dir, "foo.pb.go", `package missingpbts
87
+
88
+ type Foo struct{}
89
+ `)
90
+
91
+ comp, err := NewCompiler(&Config{
92
+ Dir: dir,
93
+ OutputPath: filepath.Join(dir, "out"),
94
+ ProtobufTypeScriptBinding: true,
95
+ }, nil, nil)
96
+ if err != nil {
97
+ t.Fatal(err)
98
+ }
99
+ result, err := comp.CompilePackages(context.Background(), ".")
100
+ if err == nil {
101
+ t.Fatal("expected missing sibling .pb.ts to fail")
102
+ }
103
+ if result == nil {
104
+ t.Fatal("expected diagnostics result")
105
+ }
106
+ for _, diag := range result.Diagnostics {
107
+ if diag.Code == "goscript/protobuf-ts-binding:missing" {
108
+ return
109
+ }
110
+ }
111
+ t.Fatalf("missing protobuf binding diagnostic not found: %#v", result.Diagnostics)
112
+ }
113
+
114
+ func TestProtobufTypeScriptBindingSkipsFilesOutsideSourceRoot(t *testing.T) {
115
+ dir := t.TempDir()
116
+ outside := filepath.Join(t.TempDir(), "outside.pb.go")
117
+ writeTestFile(t, dir, "go.mod", "module example.test/outsidepb\n\ngo 1.25\n")
118
+ writeTestFile(t, dir, "use.go", `package outsidepb
119
+ `)
120
+
121
+ semPkg := &semanticPackage{
122
+ pkgPath: "example.test/outsidepb",
123
+ source: &packages.Package{
124
+ CompiledGoFiles: []string{outside},
125
+ GoFiles: []string{outside},
126
+ Syntax: make([]*ast.File, 1),
127
+ },
128
+ }
129
+ bindings, diagnostics := protobufTypeScriptBindings(semPkg, LoweringOptions{
130
+ SourceRoot: dir,
131
+ OutputPath: filepath.Join(dir, "out"),
132
+ ProtobufTypeScriptBinding: true,
133
+ })
134
+ if len(diagnostics) != 0 {
135
+ t.Fatalf("outside source root diagnostics = %#v", diagnostics)
136
+ }
137
+ if len(bindings) != 0 {
138
+ t.Fatalf("outside source root bindings = %#v", bindings)
139
+ }
140
+ }
141
+
142
+ func TestProtobufTypeScriptBindingRootFindsParentModule(t *testing.T) {
143
+ dir := t.TempDir()
144
+ writeTestFile(t, dir, "go.mod", "module example.test/root\n\ngo 1.25\n")
145
+ nested := filepath.Join(dir, ".bldr", "build", "plugin")
146
+ if err := os.MkdirAll(nested, 0o755); err != nil {
147
+ t.Fatal(err)
148
+ }
149
+ if got := protobufTypeScriptBindingRoot(nested); got != dir {
150
+ t.Fatalf("binding root = %q, want %q", got, dir)
151
+ }
152
+ }
153
+
154
+ func writeTestFile(t *testing.T, root, rel, data string) {
155
+ t.Helper()
156
+ path := filepath.Join(root, rel)
157
+ if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
158
+ t.Fatal(err)
159
+ }
160
+ if err := os.WriteFile(path, []byte(data), 0o644); err != nil {
161
+ t.Fatal(err)
162
+ }
163
+ }
164
+
165
+ func readTestFile(t *testing.T, path string) string {
166
+ t.Helper()
167
+ data, err := os.ReadFile(path)
168
+ if err != nil {
169
+ t.Fatal(err)
170
+ }
171
+ return string(data)
172
+ }