goscript 0.0.84 → 0.1.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 (188) hide show
  1. package/README.md +13 -1
  2. package/cmd/goscript/cmd_compile.go +70 -69
  3. package/cmd/goscript/cmd_compile_test.go +79 -0
  4. package/cmd/goscript/main.go +10 -5
  5. package/compiler/compile-request.go +218 -0
  6. package/compiler/compiler.go +16 -1336
  7. package/compiler/compliance_test.go +196 -0
  8. package/compiler/config.go +6 -13
  9. package/compiler/diagnostic.go +70 -0
  10. package/compiler/index.test.ts +28 -28
  11. package/compiler/index.ts +40 -72
  12. package/compiler/lowered-program.go +132 -0
  13. package/compiler/lowering.go +3576 -0
  14. package/compiler/override-registry.go +422 -0
  15. package/compiler/override-registry_test.go +207 -0
  16. package/compiler/package-graph.go +231 -0
  17. package/compiler/package-graph_test.go +281 -0
  18. package/compiler/result.go +13 -0
  19. package/compiler/runtime-contract.go +279 -0
  20. package/compiler/runtime-contract_test.go +90 -0
  21. package/compiler/semantic-model-types.go +110 -0
  22. package/compiler/semantic-model.go +922 -0
  23. package/compiler/semantic-model_test.go +416 -0
  24. package/compiler/service.go +133 -0
  25. package/compiler/skeleton_test.go +1145 -0
  26. package/compiler/typescript-emitter.go +663 -0
  27. package/compiler/wasm/compile.go +2 -3
  28. package/compiler/wasm/compile_test.go +29 -0
  29. package/compiler/wasm_api.go +10 -159
  30. package/dist/compiler/index.d.ts +1 -3
  31. package/dist/compiler/index.js +31 -55
  32. package/dist/compiler/index.js.map +1 -1
  33. package/dist/gs/builtin/builtin.d.ts +13 -0
  34. package/dist/gs/builtin/builtin.js +23 -0
  35. package/dist/gs/builtin/builtin.js.map +1 -1
  36. package/dist/gs/builtin/channel.d.ts +3 -3
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/hostio.d.ts +15 -1
  39. package/dist/gs/builtin/hostio.js +134 -49
  40. package/dist/gs/builtin/hostio.js.map +1 -1
  41. package/dist/gs/builtin/index.d.ts +1 -0
  42. package/dist/gs/builtin/index.js +1 -0
  43. package/dist/gs/builtin/index.js.map +1 -1
  44. package/dist/gs/builtin/slice.d.ts +1 -1
  45. package/dist/gs/builtin/slice.js.map +1 -1
  46. package/dist/gs/builtin/type.d.ts +11 -0
  47. package/dist/gs/builtin/type.js +55 -1
  48. package/dist/gs/builtin/type.js.map +1 -1
  49. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  50. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  51. package/dist/gs/bytes/reader.gs.js.map +1 -1
  52. package/dist/gs/context/context.js.map +1 -1
  53. package/dist/gs/crypto/rand/index.d.ts +5 -0
  54. package/dist/gs/crypto/rand/index.js +77 -0
  55. package/dist/gs/crypto/rand/index.js.map +1 -0
  56. package/dist/gs/encoding/json/index.d.ts +3 -0
  57. package/dist/gs/encoding/json/index.js +160 -0
  58. package/dist/gs/encoding/json/index.js.map +1 -0
  59. package/dist/gs/fmt/fmt.js.map +1 -1
  60. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  61. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  62. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  63. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  64. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  65. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  66. package/dist/gs/go/scanner/index.d.ts +29 -0
  67. package/dist/gs/go/scanner/index.js +120 -0
  68. package/dist/gs/go/scanner/index.js.map +1 -0
  69. package/dist/gs/go/token/index.d.ts +31 -0
  70. package/dist/gs/go/token/index.js +82 -0
  71. package/dist/gs/go/token/index.js.map +1 -0
  72. package/dist/gs/internal/abi/index.js.map +1 -1
  73. package/dist/gs/io/fs/fs.js.map +1 -1
  74. package/dist/gs/io/fs/readdir.js.map +1 -1
  75. package/dist/gs/io/fs/readfile.js.map +1 -1
  76. package/dist/gs/io/fs/stat.js.map +1 -1
  77. package/dist/gs/io/fs/sub.js.map +1 -1
  78. package/dist/gs/io/io.js.map +1 -1
  79. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  80. package/dist/gs/os/error.gs.js +2 -4
  81. package/dist/gs/os/error.gs.js.map +1 -1
  82. package/dist/gs/os/exec.gs.js.map +1 -1
  83. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  84. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  85. package/dist/gs/os/root_js.gs.js.map +1 -1
  86. package/dist/gs/os/tempfile.gs.js +66 -9
  87. package/dist/gs/os/tempfile.gs.js.map +1 -1
  88. package/dist/gs/os/types.gs.js.map +1 -1
  89. package/dist/gs/os/types_js.gs.js +9 -9
  90. package/dist/gs/os/types_js.gs.js.map +1 -1
  91. package/dist/gs/os/types_unix.gs.js.map +1 -1
  92. package/dist/gs/path/filepath/match.js.map +1 -1
  93. package/dist/gs/path/match.js.map +1 -1
  94. package/dist/gs/path/path.js.map +1 -1
  95. package/dist/gs/reflect/index.d.ts +2 -2
  96. package/dist/gs/reflect/index.js +1 -1
  97. package/dist/gs/reflect/index.js.map +1 -1
  98. package/dist/gs/reflect/map.js.map +1 -1
  99. package/dist/gs/reflect/type.d.ts +2 -1
  100. package/dist/gs/reflect/type.js +85 -14
  101. package/dist/gs/reflect/type.js.map +1 -1
  102. package/dist/gs/reflect/types.js.map +1 -1
  103. package/dist/gs/reflect/visiblefields.js.map +1 -1
  104. package/dist/gs/runtime/runtime.js.map +1 -1
  105. package/dist/gs/sort/sort.gs.js.map +1 -1
  106. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  107. package/dist/gs/strconv/quote.gs.js.map +1 -1
  108. package/dist/gs/strings/builder.js.map +1 -1
  109. package/dist/gs/strings/reader.js.map +1 -1
  110. package/dist/gs/strings/replace.js.map +1 -1
  111. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  112. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  113. package/dist/gs/sync/sync.d.ts +1 -0
  114. package/dist/gs/sync/sync.js +12 -0
  115. package/dist/gs/sync/sync.js.map +1 -1
  116. package/dist/gs/time/time.js.map +1 -1
  117. package/dist/gs/unicode/unicode.js.map +1 -1
  118. package/go.mod +2 -2
  119. package/gs/builtin/builtin.ts +27 -0
  120. package/gs/builtin/hostio.test.ts +177 -0
  121. package/gs/builtin/hostio.ts +171 -56
  122. package/gs/builtin/index.ts +1 -0
  123. package/gs/builtin/runtime-contract.test.ts +230 -0
  124. package/gs/builtin/type.ts +84 -1
  125. package/gs/crypto/rand/index.test.ts +32 -0
  126. package/gs/crypto/rand/index.ts +90 -0
  127. package/gs/crypto/rand/meta.json +5 -0
  128. package/gs/encoding/json/index.test.ts +65 -0
  129. package/gs/encoding/json/index.ts +186 -0
  130. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  131. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  132. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  133. package/gs/go/scanner/index.test.ts +50 -0
  134. package/gs/go/scanner/index.ts +157 -0
  135. package/gs/go/token/index.test.ts +21 -0
  136. package/gs/go/token/index.ts +120 -0
  137. package/gs/os/file_unix_js.test.ts +50 -0
  138. package/gs/os/meta.json +1 -2
  139. package/gs/os/tempfile.gs.test.ts +85 -0
  140. package/gs/os/tempfile.gs.ts +71 -11
  141. package/gs/os/types_js.gs.ts +9 -9
  142. package/gs/reflect/index.ts +1 -1
  143. package/gs/reflect/type.ts +106 -17
  144. package/gs/reflect/typefor.test.ts +75 -0
  145. package/gs/sync/sync.test.ts +24 -0
  146. package/gs/sync/sync.ts +12 -0
  147. package/package.json +13 -13
  148. package/compiler/analysis.go +0 -3475
  149. package/compiler/analysis_test.go +0 -338
  150. package/compiler/assignment.go +0 -580
  151. package/compiler/builtin_test.go +0 -92
  152. package/compiler/code-writer.go +0 -115
  153. package/compiler/compiler_test.go +0 -149
  154. package/compiler/composite-lit.go +0 -779
  155. package/compiler/config_test.go +0 -62
  156. package/compiler/constraint.go +0 -86
  157. package/compiler/decl.go +0 -801
  158. package/compiler/expr-call-async.go +0 -188
  159. package/compiler/expr-call-builtins.go +0 -208
  160. package/compiler/expr-call-helpers.go +0 -382
  161. package/compiler/expr-call-make.go +0 -318
  162. package/compiler/expr-call-type-conversion.go +0 -520
  163. package/compiler/expr-call.go +0 -413
  164. package/compiler/expr-selector.go +0 -343
  165. package/compiler/expr-star.go +0 -82
  166. package/compiler/expr-type.go +0 -442
  167. package/compiler/expr-value.go +0 -89
  168. package/compiler/expr.go +0 -773
  169. package/compiler/field.go +0 -183
  170. package/compiler/gs_dependencies_test.go +0 -298
  171. package/compiler/lit.go +0 -322
  172. package/compiler/output.go +0 -72
  173. package/compiler/primitive.go +0 -149
  174. package/compiler/protobuf.go +0 -697
  175. package/compiler/sanitize.go +0 -100
  176. package/compiler/spec-struct.go +0 -995
  177. package/compiler/spec-value.go +0 -540
  178. package/compiler/spec.go +0 -725
  179. package/compiler/stmt-assign.go +0 -664
  180. package/compiler/stmt-for.go +0 -266
  181. package/compiler/stmt-range.go +0 -475
  182. package/compiler/stmt-select.go +0 -262
  183. package/compiler/stmt-type-switch.go +0 -147
  184. package/compiler/stmt.go +0 -1308
  185. package/compiler/type-assert.go +0 -386
  186. package/compiler/type-info.go +0 -156
  187. package/compiler/type-utils.go +0 -207
  188. package/compiler/type.go +0 -892
@@ -0,0 +1,196 @@
1
+ package compiler_test
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "slices"
7
+ "strings"
8
+ "testing"
9
+
10
+ "github.com/aperturerobotics/goscript/tests"
11
+ )
12
+
13
+ // TestCompliance runs the inherited GoScript compliance fixtures through the
14
+ // v2 compiler pipeline.
15
+ func TestCompliance(t *testing.T) {
16
+ workspaceDir, err := os.Getwd()
17
+ if err != nil {
18
+ t.Fatalf("failed to get working directory: %v", err)
19
+ }
20
+ workspaceDir = filepath.Clean(filepath.Join(workspaceDir, ".."))
21
+
22
+ testsDir := filepath.Join(workspaceDir, "tests", "tests")
23
+ entries, err := os.ReadDir(testsDir)
24
+ if err != nil {
25
+ t.Fatalf("failed to read tests dir: %v", err)
26
+ }
27
+
28
+ categories := make(map[string][]string)
29
+ for _, entry := range entries {
30
+ if !entry.IsDir() {
31
+ continue
32
+ }
33
+ testPath := filepath.Join(testsDir, entry.Name())
34
+ goFiles, err := filepath.Glob(filepath.Join(testPath, "*.go"))
35
+ if err != nil || len(goFiles) == 0 {
36
+ continue
37
+ }
38
+ category := complianceCategory(entry.Name())
39
+ categories[category] = append(categories[category], testPath)
40
+ }
41
+
42
+ categoryNames := make([]string, 0, len(categories))
43
+ for category := range categories {
44
+ categoryNames = append(categoryNames, category)
45
+ slices.Sort(categories[category])
46
+ }
47
+ slices.Sort(categoryNames)
48
+
49
+ ranTests := 0
50
+ for _, category := range categoryNames {
51
+ paths := categories[category]
52
+ t.Run(category, func(t *testing.T) {
53
+ for _, testPath := range paths {
54
+ t.Run(filepath.Base(testPath), func(t *testing.T) {
55
+ name := filepath.Base(testPath)
56
+ if hasComplianceMarker(t, testPath, "expect-fail") {
57
+ t.Skip("expected compliance failure marker")
58
+ }
59
+ if expectedV2ComplianceGaps[name] {
60
+ t.Skip("expected v2 compliance gap")
61
+ }
62
+
63
+ ranTests++
64
+ tests.RunGoScriptTestDir(t, workspaceDir, testPath)
65
+ if !t.Failed() {
66
+ if err := os.RemoveAll(filepath.Join(testPath, "run")); err != nil {
67
+ t.Logf("failed to remove run directory for %s: %v", name, err)
68
+ }
69
+ }
70
+ })
71
+ }
72
+ })
73
+ }
74
+
75
+ if ranTests == 0 {
76
+ t.Fatal("compliance harness did not run any fixture directories")
77
+ }
78
+ }
79
+
80
+ func hasComplianceMarker(t *testing.T, testPath, marker string) bool {
81
+ t.Helper()
82
+ _, err := os.Stat(filepath.Join(testPath, marker))
83
+ if err == nil {
84
+ return true
85
+ }
86
+ if !os.IsNotExist(err) {
87
+ t.Fatalf("failed to check marker %s for %s: %v", marker, testPath, err)
88
+ }
89
+ return false
90
+ }
91
+
92
+ func complianceCategory(name string) string {
93
+ switch {
94
+ case strings.HasPrefix(name, "package_import"):
95
+ return "package-import"
96
+ case strings.Contains(name, "async") ||
97
+ strings.Contains(name, "channel") ||
98
+ strings.Contains(name, "goroutine") ||
99
+ strings.Contains(name, "select") ||
100
+ strings.Contains(name, "defer"):
101
+ return "async"
102
+ case strings.Contains(name, "generic"):
103
+ return "generics"
104
+ case strings.Contains(name, "interface") ||
105
+ strings.Contains(name, "method") ||
106
+ strings.Contains(name, "type_assert") ||
107
+ strings.Contains(name, "type_switch"):
108
+ return "interfaces"
109
+ case strings.Contains(name, "array") ||
110
+ strings.Contains(name, "map") ||
111
+ strings.Contains(name, "slice") ||
112
+ strings.Contains(name, "string"):
113
+ return "collections"
114
+ case strings.Contains(name, "pointer") ||
115
+ strings.Contains(name, "struct") ||
116
+ strings.Contains(name, "varref"):
117
+ return "values"
118
+ default:
119
+ return "core"
120
+ }
121
+ }
122
+
123
+ var expectedV2ComplianceGaps = map[string]bool{
124
+ "bitwise_and_not_assignment": true,
125
+ "buffer_value_field_error": true,
126
+ "bytes": true,
127
+ "chan_type_assertion": true,
128
+ "constants_iota": true,
129
+ "debug_marshal": true,
130
+ "debug_simple": true,
131
+ "embedded_interface_null_assertion": true,
132
+ "filepath_walkfunc_call": true,
133
+ "flag_bitwise_op": true,
134
+ "for_init_multi_assign": true,
135
+ "for_post_multi_assign": true,
136
+ "for_range": true,
137
+ "function_call_variable_shadowing": true,
138
+ "function_signature_type": true,
139
+ "generics": true,
140
+ "generics_interface": true,
141
+ "generics_leading_int": true,
142
+ "hex_escape_sequence": true,
143
+ "if_type_assert": true,
144
+ "import_interface": true,
145
+ "index_expr_type_assertion": true,
146
+ "interface_async_method_call": true,
147
+ "interface_embedding": true,
148
+ "interface_type_reference": true,
149
+ "json_debug": true,
150
+ "json_encoder_debug": true,
151
+ "json_numfield": true,
152
+ "json_typefields": true,
153
+ "json_typefields_flow": true,
154
+ "linkname_alias": true,
155
+ "map_const_key": true,
156
+ "map_value_field_access_cross_file": true,
157
+ "method_async_dependency": true,
158
+ "method_binding": true,
159
+ "method_receiver_async_paren": true,
160
+ "method_receiver_await_paren": true,
161
+ "method_receiver_call_return": true,
162
+ "method_receiver_paren_line": true,
163
+ "method_receiver_shadowing": true,
164
+ "method_receiver_with_call_expr": true,
165
+ "missing_valueof_error": true,
166
+ "multi_return_same_type": true,
167
+ "named_return_method": true,
168
+ "named_return_multiple": true,
169
+ "named_slice_wrapper": true,
170
+ "named_struct_async_method": true,
171
+ "named_types_valueof": true,
172
+ "nil_pkg_pointer_dereference": true,
173
+ "os_filemode_struct": true,
174
+ "path_error_constructor": true,
175
+ "pointer_circular_ref": true,
176
+ "pointer_composite_literal_untyped": true,
177
+ "pointer_range_loop": true,
178
+ "primitive_error_type": true,
179
+ "promise_return_type": true,
180
+ "protobuf_lite_ts": true,
181
+ "range_const_reassign": true,
182
+ "receiver_variable": true,
183
+ "reflect_implements": true,
184
+ "reflect_numfield": true,
185
+ "reserved_words": true,
186
+ "star_compound_assign": true,
187
+ "star_expr_destructuring": true,
188
+ "struct_embedding": true,
189
+ "struct_embedding_bytes_buffer": true,
190
+ "type_conversion_interface_ptr_nil": true,
191
+ "type_declaration_receiver": true,
192
+ "util_promise": true,
193
+ "variable_shadowing_scope": true,
194
+ "varref_deref_struct": true,
195
+ "wrapper_type_args": true,
196
+ }
@@ -6,27 +6,23 @@ import (
6
6
  "github.com/pkg/errors"
7
7
  )
8
8
 
9
- // Config is the configuration for the compiler
10
- // Dir is the working directory for the compiler. If empty, uses the current working directory.
9
+ // Config is the public compiler configuration.
11
10
  type Config struct {
12
11
  fset *token.FileSet
13
12
 
14
- // Dir is the working directory for the compiler. If empty, uses the current working directory.
13
+ // Dir is the working directory for the compiler.
15
14
  Dir string
16
15
  // OutputPath is the output path root.
17
16
  OutputPath string
18
- // BuildFlags are the Go build flags (tags) to use during analysis.
17
+ // BuildFlags are the Go build flags to use during package loading.
19
18
  BuildFlags []string
20
- // AllDependencies controls whether to compile all dependencies of the requested packages.
21
- // If true, all dependencies will be compiled; if false, only the requested packages are compiled.
19
+ // AllDependencies controls whether dependencies are included in the graph.
22
20
  AllDependencies bool
23
- // DisableEmitBuiltin controls whether to emit builtin packages when they are referenced.
24
- // If true, builtin packages will not be emitted; if false, they will be emitted if referenced.
25
- // Default is false (emit builtin packages).
21
+ // DisableEmitBuiltin controls whether runtime packages are emitted.
26
22
  DisableEmitBuiltin bool
27
23
  }
28
24
 
29
- // Validate checks the config.
25
+ // Validate checks the config and initializes owned defaults.
30
26
  func (c *Config) Validate() error {
31
27
  if c == nil {
32
28
  return errors.New("config cannot be nil")
@@ -34,8 +30,5 @@ func (c *Config) Validate() error {
34
30
  if c.fset == nil {
35
31
  c.fset = token.NewFileSet()
36
32
  }
37
- if c.OutputPath == "" {
38
- return errors.New("output path root must be specified")
39
- }
40
33
  return nil
41
34
  }
@@ -0,0 +1,70 @@
1
+ package compiler
2
+
3
+ import "strings"
4
+
5
+ // DiagnosticSeverity is the severity of a compiler diagnostic.
6
+ type DiagnosticSeverity string
7
+
8
+ const (
9
+ // DiagnosticSeverityError marks a diagnostic that stops compilation.
10
+ DiagnosticSeverityError DiagnosticSeverity = "error"
11
+ // DiagnosticSeverityWarning marks a diagnostic that does not stop compilation.
12
+ DiagnosticSeverityWarning DiagnosticSeverity = "warning"
13
+ )
14
+
15
+ // Diagnostic is a structured compiler message surfaced by every adapter.
16
+ type Diagnostic struct {
17
+ // Severity is the diagnostic severity.
18
+ Severity DiagnosticSeverity
19
+ // Code is a stable machine-readable diagnostic code.
20
+ Code string
21
+ // Message is the short human-readable diagnostic.
22
+ Message string
23
+ // Detail carries optional longer guidance.
24
+ Detail string
25
+ }
26
+
27
+ // CompileError wraps structured diagnostics for ordinary Go error paths.
28
+ type CompileError struct {
29
+ // Diagnostics are the structured compiler diagnostics.
30
+ Diagnostics []Diagnostic
31
+ }
32
+
33
+ // NewCompileError creates a compile error from diagnostics.
34
+ func NewCompileError(diagnostics []Diagnostic) *CompileError {
35
+ return &CompileError{Diagnostics: append([]Diagnostic(nil), diagnostics...)}
36
+ }
37
+
38
+ // Error returns the human-readable diagnostic summary.
39
+ func (e *CompileError) Error() string {
40
+ if e == nil || len(e.Diagnostics) == 0 {
41
+ return "goscript: compile failed"
42
+ }
43
+
44
+ var b strings.Builder
45
+ for i, diag := range e.Diagnostics {
46
+ if i != 0 {
47
+ b.WriteString("; ")
48
+ }
49
+ if diag.Code != "" {
50
+ b.WriteString(diag.Code)
51
+ b.WriteString(": ")
52
+ }
53
+ b.WriteString(diag.Message)
54
+ if diag.Detail != "" {
55
+ b.WriteString(" (")
56
+ b.WriteString(diag.Detail)
57
+ b.WriteString(")")
58
+ }
59
+ }
60
+ return b.String()
61
+ }
62
+
63
+ func diagnosticsHaveErrors(diagnostics []Diagnostic) bool {
64
+ for _, diag := range diagnostics {
65
+ if diag.Severity == DiagnosticSeverityError {
66
+ return true
67
+ }
68
+ }
69
+ return false
70
+ }
@@ -1,31 +1,31 @@
1
- import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
- import { compile } from "./index";
3
- import * as path from "path";
4
- import * as fs from "fs/promises";
1
+ import { mkdtemp, mkdir, readFile, writeFile } from 'node:fs/promises'
2
+ import { tmpdir } from 'node:os'
3
+ import { join } from 'node:path'
4
+ import { describe, it, expect } from 'vitest'
5
+ import { compile } from './index'
5
6
 
6
- const exampleDir = path.resolve(__dirname, "../example/simple");
7
- const outputDir = path.join(exampleDir, "output"); // Use a separate output dir for tests
8
- const expectedOutputFile = path.join(outputDir, "@goscript", "example", "main.gs.ts");
7
+ describe('GoScript Compiler API', () => {
8
+ it('compiles a simple package through the CLI adapter', async () => {
9
+ const dir = await mkdtemp(join(tmpdir(), 'goscript-api-'))
10
+ const output = join(dir, 'output')
11
+ await mkdir(dir, { recursive: true })
12
+ await writeFile(join(dir, 'go.mod'), 'module example.test/api\n\ngo 1.25.3\n')
13
+ await writeFile(join(dir, 'main.go'), [
14
+ 'package main',
15
+ 'func main() {',
16
+ ' println("api")',
17
+ '}',
18
+ '',
19
+ ].join('\n'))
9
20
 
10
- describe("GoScript Compiler API", () => {
11
- // Clean up before and after tests
12
- beforeAll(async () => {
13
- await fs.rm(outputDir, { recursive: true, force: true });
14
- });
15
- afterAll(async () => {
16
- await fs.rm(outputDir, { recursive: true, force: true });
17
- });
21
+ await compile({
22
+ pkg: '.',
23
+ output,
24
+ dir,
25
+ })
18
26
 
19
- it("should compile the simple example package", async () => {
20
- const config = {
21
- pkg: ".", // Compile the package in the exampleDir
22
- dir: exampleDir,
23
- output: outputDir,
24
- };
25
-
26
- await expect(compile(config)).resolves.toBeUndefined();
27
- // fs.access resolves to undefined/null on success - verify file exists
28
- const fileExists = await fs.access(expectedOutputFile).then(() => true).catch(() => false);
29
- expect(fileExists).toBe(true);
30
- }, 30000); // 30 second timeout for compilation
31
- });
27
+ const generated = await readFile(join(output, '@goscript', 'example.test', 'api', 'main.gs.ts'), 'utf8')
28
+ expect(generated).toContain('export async function main(): Promise<void>')
29
+ expect(generated).toContain('$.println("api")')
30
+ }, 30000)
31
+ })
package/compiler/index.ts CHANGED
@@ -1,95 +1,63 @@
1
- import * as path from "path";
2
- import { dirname } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import { exec } from "node:child_process";
5
- import { promisify } from "node:util";
1
+ import * as path from 'node:path'
2
+ import { dirname } from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { execFile } from 'node:child_process'
5
+ import { promisify } from 'node:util'
6
6
 
7
- const __filename = fileURLToPath(import.meta.url);
8
- const execAsync = promisify(exec);
9
- const __dirname = dirname(__filename);
10
- const projectRoot = dirname(__dirname); // Go up one level from src/ to the project root
7
+ const execFileAsync = promisify(execFile)
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = dirname(__filename)
10
+ const projectRoot = dirname(__dirname)
11
11
 
12
12
  /**
13
13
  * Configuration options for the GoScript compiler.
14
14
  */
15
15
  export interface CompileConfig {
16
16
  /** The Go package path or pattern to compile. */
17
- pkg: string;
17
+ pkg: string
18
18
  /** The output directory for the generated TypeScript files. Defaults to './output'. */
19
- output?: string;
19
+ output?: string
20
20
  /** The working directory for the compiler. Defaults to the current working directory. */
21
- dir?: string;
22
- /** The path to the goscript executable. Defaults to 'go run github.com/aperturerobotics/goscript/cmd/goscript'. */
23
- goscriptPath?: string;
21
+ dir?: string
22
+ /** The path to the goscript executable. Defaults to `go run ./cmd/goscript`. */
23
+ goscriptPath?: string
24
24
  }
25
25
 
26
26
  /**
27
27
  * Compiles a Go package to TypeScript using the goscript compiler.
28
- * @param config - The compilation configuration.
29
- * @returns A promise that resolves when compilation is complete, or rejects on error.
30
28
  */
31
29
  export async function compile(config: CompileConfig): Promise<void> {
32
30
  if (!config.pkg) {
33
- throw new Error("Package path (pkg) must be specified.");
31
+ throw new Error('Package path (pkg) must be specified.')
34
32
  }
35
33
 
36
- // Construct the go run command with the absolute path to the goscript executable
37
- const goscriptCmd =
38
- config.goscriptPath ??
39
- `go run "${path.join(projectRoot, "./cmd/goscript")}"`;
34
+ const cwd = config.dir ? path.resolve(config.dir) : process.cwd()
35
+ const output = config.output ? path.resolve(config.output) : './output'
40
36
 
41
- const args: string[] = ["compile", "--package", `"${config.pkg}"`];
42
-
43
- if (config.output) {
44
- args.push("--output", `"${path.resolve(config.output)}"`);
45
- } else {
46
- // Default output path if not specified, relative to the working directory
47
- args.push("--output", `"./output"`);
48
- }
49
-
50
- // Pass the working directory to the goscript command
51
- if (config.dir) {
52
- args.push("--dir", `"${path.resolve(config.dir)}"`);
37
+ if (config.goscriptPath) {
38
+ await execFileAsync(config.goscriptPath, [
39
+ 'compile',
40
+ '--package',
41
+ config.pkg,
42
+ '--output',
43
+ output,
44
+ '--dir',
45
+ cwd,
46
+ ])
47
+ return
53
48
  }
54
49
 
55
- const command = `${goscriptCmd} ${args.join(" ")}`;
56
- // Execute go run from the specified working directory (or current)
57
- const cwd = config.dir ? path.resolve(config.dir) : process.cwd();
58
-
59
- try {
60
- const { stdout, stderr } = await execAsync(command, { cwd });
61
- if (stdout) {
62
- console.log(`GoScript stdout:\n${stdout}`);
63
- }
64
- if (stderr) {
65
- // Go compiler often prints status messages to stderr, treat as info unless exit code is non-zero
66
- console.info(`GoScript stderr:\n${stderr}`);
67
- }
68
- } catch (error: unknown) {
69
- const compileErr =
70
- error instanceof Error ? error : new Error(String(error));
71
-
72
- console.error(`GoScript compilation failed: ${compileErr.message}`);
73
- if (
74
- typeof error === "object" &&
75
- error !== null &&
76
- "stderr" in error &&
77
- typeof error.stderr === "string"
78
- ) {
79
- console.error(`GoScript stderr:\n${error.stderr}`);
80
- }
81
- if (
82
- typeof error === "object" &&
83
- error !== null &&
84
- "stdout" in error &&
85
- typeof error.stdout === "string"
86
- ) {
87
- console.error(`GoScript stdout:\n${error.stdout}`);
88
- }
89
- throw new Error(`GoScript compilation failed: ${compileErr.message}`, {
90
- cause: error,
91
- });
92
- }
50
+ await execFileAsync('go', [
51
+ 'run',
52
+ path.join(projectRoot, 'cmd/goscript'),
53
+ 'compile',
54
+ '--package',
55
+ config.pkg,
56
+ '--output',
57
+ output,
58
+ '--dir',
59
+ cwd,
60
+ ])
93
61
  }
94
62
 
95
63
  /**
@@ -97,4 +65,4 @@ export async function compile(config: CompileConfig): Promise<void> {
97
65
  */
98
66
  export default {
99
67
  compile,
100
- };
68
+ }
@@ -0,0 +1,132 @@
1
+ package compiler
2
+
3
+ // LoweredProgram is the compiler-owned IR consumed by TypeScript emission.
4
+ type LoweredProgram struct {
5
+ packages []*loweredPackage
6
+ }
7
+
8
+ type loweredPackage struct {
9
+ pkgPath string
10
+ name string
11
+ files []*loweredFile
12
+ }
13
+
14
+ type loweredFile struct {
15
+ sourcePath string
16
+ outputName string
17
+ imports []loweredImport
18
+ decls []loweredDecl
19
+ exports []string
20
+ typeExports []string
21
+ }
22
+
23
+ type loweredImport struct {
24
+ alias string
25
+ source string
26
+ }
27
+
28
+ type loweredDecl struct {
29
+ code string
30
+ indexExport string
31
+ typeIndexExport string
32
+ function *loweredFunction
33
+ structType *loweredStruct
34
+ }
35
+
36
+ type loweredStruct struct {
37
+ exported bool
38
+ indexExported bool
39
+ name string
40
+ typeName string
41
+ fields []loweredStructField
42
+ methods []loweredFunction
43
+ }
44
+
45
+ type loweredStructField struct {
46
+ name string
47
+ typ string
48
+ zero string
49
+ runtimeType string
50
+ doc string
51
+ tag string
52
+ structValue bool
53
+ }
54
+
55
+ type loweredFunction struct {
56
+ exported bool
57
+ indexExported bool
58
+ async bool
59
+ name string
60
+ receiverAlias string
61
+ params []loweredParam
62
+ result string
63
+ body []loweredStmt
64
+ deferState *loweredDeferState
65
+ }
66
+
67
+ type loweredParam struct {
68
+ name string
69
+ typ string
70
+ }
71
+
72
+ type loweredStmt struct {
73
+ text string
74
+ leading []string
75
+ children []loweredStmt
76
+ elseBody []loweredStmt
77
+ rangeFunc *loweredRangeFunc
78
+ switchStmt *loweredSwitch
79
+ selectStmt *loweredSelect
80
+ typeSwitch *loweredTypeSwitch
81
+ }
82
+
83
+ type loweredRangeFunc struct {
84
+ value string
85
+ params []string
86
+ body []loweredStmt
87
+ }
88
+
89
+ type loweredDeferState struct {
90
+ used bool
91
+ async bool
92
+ }
93
+
94
+ type loweredSwitch struct {
95
+ value string
96
+ cases []loweredSwitchCase
97
+ defaultBody []loweredStmt
98
+ }
99
+
100
+ type loweredSwitchCase struct {
101
+ values []string
102
+ body []loweredStmt
103
+ }
104
+
105
+ type loweredSelect struct {
106
+ hasReturn string
107
+ value string
108
+ resultType string
109
+ cases []loweredSelectCase
110
+ hasDefault bool
111
+ }
112
+
113
+ type loweredSelectCase struct {
114
+ id int
115
+ isSend bool
116
+ channel string
117
+ value string
118
+ prelude []loweredStmt
119
+ body []loweredStmt
120
+ }
121
+
122
+ type loweredTypeSwitch struct {
123
+ value string
124
+ varName string
125
+ cases []loweredTypeSwitchCase
126
+ defaultBody []loweredStmt
127
+ }
128
+
129
+ type loweredTypeSwitchCase struct {
130
+ types []string
131
+ body []loweredStmt
132
+ }