goscript 0.0.2

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 (160) hide show
  1. package/.aider-prompt +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +427 -0
  4. package/builtin/builtin.ts +507 -0
  5. package/cmd/goscript/cmd_compile.go +59 -0
  6. package/cmd/goscript/main.go +23 -0
  7. package/compiler/compile.go +183 -0
  8. package/compiler/compile_comment.go +41 -0
  9. package/compiler/compile_decls.go +72 -0
  10. package/compiler/compile_expr.go +831 -0
  11. package/compiler/compile_field.go +89 -0
  12. package/compiler/compile_spec.go +256 -0
  13. package/compiler/compile_stmt.go +1509 -0
  14. package/compiler/compiler.go +81 -0
  15. package/compiler/config.go +32 -0
  16. package/compiler/context.go +9 -0
  17. package/compiler/file_compiler.go +80 -0
  18. package/compiler/output_path.go +31 -0
  19. package/compiler/pkg_compiler.go +73 -0
  20. package/compiler/writer.go +90 -0
  21. package/compliance/COMPLIANCE.md +133 -0
  22. package/compliance/compliance.go +313 -0
  23. package/compliance/compliance_test.go +57 -0
  24. package/compliance/tests/array_literal/array_literal.go +15 -0
  25. package/compliance/tests/array_literal/array_literal.gs.ts +19 -0
  26. package/compliance/tests/array_literal/expected.log +3 -0
  27. package/compliance/tests/async_basic/async_basic.go +26 -0
  28. package/compliance/tests/async_basic/async_basic.gs.ts +30 -0
  29. package/compliance/tests/async_basic/expected.log +1 -0
  30. package/compliance/tests/basic_arithmetic/basic_arithmetic.go +15 -0
  31. package/compliance/tests/basic_arithmetic/basic_arithmetic.gs.ts +19 -0
  32. package/compliance/tests/basic_arithmetic/expected.log +5 -0
  33. package/compliance/tests/boolean_logic/boolean_logic.go +13 -0
  34. package/compliance/tests/boolean_logic/boolean_logic.gs.ts +17 -0
  35. package/compliance/tests/boolean_logic/expected.log +3 -0
  36. package/compliance/tests/channel_basic/channel_basic.go +12 -0
  37. package/compliance/tests/channel_basic/channel_basic.gs.ts +18 -0
  38. package/compliance/tests/channel_basic/expected.log +1 -0
  39. package/compliance/tests/composite_literal_assignment/composite_literal_assignment.go +20 -0
  40. package/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.ts +27 -0
  41. package/compliance/tests/composite_literal_assignment/expected.log +2 -0
  42. package/compliance/tests/constants/constants.go +18 -0
  43. package/compliance/tests/constants/constants.gs.ts +22 -0
  44. package/compliance/tests/constants/expected.log +3 -0
  45. package/compliance/tests/copy_independence/copy_independence.go +29 -0
  46. package/compliance/tests/copy_independence/copy_independence.gs.ts +36 -0
  47. package/compliance/tests/copy_independence/expected.log +4 -0
  48. package/compliance/tests/float64/expected.log +6 -0
  49. package/compliance/tests/float64/float64.go +28 -0
  50. package/compliance/tests/float64/float64.gs.ts +32 -0
  51. package/compliance/tests/for_loop_basic/expected.log +5 -0
  52. package/compliance/tests/for_loop_basic/for_loop_basic.go +9 -0
  53. package/compliance/tests/for_loop_basic/for_loop_basic.gs.ts +13 -0
  54. package/compliance/tests/for_loop_condition_only/expected.log +5 -0
  55. package/compliance/tests/for_loop_condition_only/main.go +9 -0
  56. package/compliance/tests/for_loop_condition_only/main.gs.ts +13 -0
  57. package/compliance/tests/for_range/expected.log +9 -0
  58. package/compliance/tests/for_range/for_range.go +26 -0
  59. package/compliance/tests/for_range/for_range.gs.ts +45 -0
  60. package/compliance/tests/for_range_index_use/expected.log +6 -0
  61. package/compliance/tests/for_range_index_use/for_range_index_use.go +11 -0
  62. package/compliance/tests/for_range_index_use/for_range_index_use.gs.ts +18 -0
  63. package/compliance/tests/func_literal/expected.log +1 -0
  64. package/compliance/tests/func_literal/func_literal.go +10 -0
  65. package/compliance/tests/func_literal/func_literal.gs.ts +15 -0
  66. package/compliance/tests/function_call_result_assignment/expected.log +2 -0
  67. package/compliance/tests/function_call_result_assignment/function_call_result_assignment.go +24 -0
  68. package/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.ts +31 -0
  69. package/compliance/tests/if_statement/expected.log +1 -0
  70. package/compliance/tests/if_statement/if_statement.go +11 -0
  71. package/compliance/tests/if_statement/if_statement.gs.ts +15 -0
  72. package/compliance/tests/interface_to_interface_type_assertion/expected.log +1 -0
  73. package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.go +30 -0
  74. package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.ts +41 -0
  75. package/compliance/tests/interface_type_assertion/expected.log +1 -0
  76. package/compliance/tests/interface_type_assertion/interface_type_assertion.go +26 -0
  77. package/compliance/tests/interface_type_assertion/interface_type_assertion.gs.ts +36 -0
  78. package/compliance/tests/map_support/expected.log +13 -0
  79. package/compliance/tests/map_support/map_support.go +89 -0
  80. package/compliance/tests/map_support/map_support.gs.ts +102 -0
  81. package/compliance/tests/method_call_on_pointer_receiver/expected.log +1 -0
  82. package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.go +19 -0
  83. package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.ts +27 -0
  84. package/compliance/tests/method_call_on_pointer_via_value/expected.log +1 -0
  85. package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.go +29 -0
  86. package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.ts +38 -0
  87. package/compliance/tests/method_call_on_value_receiver/expected.log +1 -0
  88. package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.go +16 -0
  89. package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.ts +24 -0
  90. package/compliance/tests/method_call_on_value_via_pointer/expected.log +2 -0
  91. package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.go +30 -0
  92. package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.ts +38 -0
  93. package/compliance/tests/multiple_return_values/expected.log +6 -0
  94. package/compliance/tests/multiple_return_values/multiple_return_values.go +19 -0
  95. package/compliance/tests/multiple_return_values/multiple_return_values.gs.ts +23 -0
  96. package/compliance/tests/pointer_assignment_no_copy/expected.log +2 -0
  97. package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.go +28 -0
  98. package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.ts +35 -0
  99. package/compliance/tests/pointer_composite_literal_assignment/expected.log +3 -0
  100. package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.go +23 -0
  101. package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.ts +30 -0
  102. package/compliance/tests/pointer_deref_multiassign/expected.log +0 -0
  103. package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.go +17 -0
  104. package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.ts +27 -0
  105. package/compliance/tests/pointer_initialization/expected.log +1 -0
  106. package/compliance/tests/pointer_initialization/pointer_initialization.go +16 -0
  107. package/compliance/tests/pointer_initialization/pointer_initialization.gs.ts +22 -0
  108. package/compliance/tests/select_receive_on_closed_channel_no_default/expected.log +1 -0
  109. package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.go +15 -0
  110. package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.ts +31 -0
  111. package/compliance/tests/select_send_on_full_buffered_channel_with_default/expected.log +1 -0
  112. package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.go +13 -0
  113. package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.ts +35 -0
  114. package/compliance/tests/select_statement/expected.log +9 -0
  115. package/compliance/tests/select_statement/select_statement.go +109 -0
  116. package/compliance/tests/select_statement/select_statement.gs.ts +239 -0
  117. package/compliance/tests/simple/expected.log +1 -0
  118. package/compliance/tests/simple/simple.go +5 -0
  119. package/compliance/tests/simple/simple.gs.ts +9 -0
  120. package/compliance/tests/simple_deref_assignment/expected.log +2 -0
  121. package/compliance/tests/simple_deref_assignment/simple_deref_assignment.go +19 -0
  122. package/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.ts +26 -0
  123. package/compliance/tests/slices/expected.log +7 -0
  124. package/compliance/tests/slices/slices.go +22 -0
  125. package/compliance/tests/slices/slices.gs.ts +26 -0
  126. package/compliance/tests/string_rune_conversion/expected.log +3 -0
  127. package/compliance/tests/string_rune_conversion/string_rune_conversion.go +16 -0
  128. package/compliance/tests/string_rune_conversion/string_rune_conversion.gs.ts +22 -0
  129. package/compliance/tests/struct_field_access/expected.log +2 -0
  130. package/compliance/tests/struct_field_access/struct_field_access.go +13 -0
  131. package/compliance/tests/struct_field_access/struct_field_access.gs.ts +20 -0
  132. package/compliance/tests/struct_value_init_clone/expected.log +5 -0
  133. package/compliance/tests/struct_value_init_clone/struct_value_init_clone.go +28 -0
  134. package/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.ts +35 -0
  135. package/compliance/tests/switch_statement/expected.log +14 -0
  136. package/compliance/tests/switch_statement/switch_statement.go +59 -0
  137. package/compliance/tests/switch_statement/switch_statement.gs.ts +85 -0
  138. package/compliance/tests/value_type_copy_behavior/expected.log +3 -0
  139. package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.go +25 -0
  140. package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.ts +34 -0
  141. package/design/DESIGN.md +599 -0
  142. package/example/simple/build.bash +10 -0
  143. package/example/simple/go.mod +23 -0
  144. package/example/simple/go.sum +39 -0
  145. package/example/simple/main.go +138 -0
  146. package/example/simple/main.gs.ts +133 -0
  147. package/example/simple/main.ts +3 -0
  148. package/example/simple/main_test.go +59 -0
  149. package/example/simple/main_tools.go +5 -0
  150. package/example/simple/package.json +7 -0
  151. package/example/simple/run.bash +6 -0
  152. package/example/simple/tsconfig.json +28 -0
  153. package/example/simple/yarn.lock +8 -0
  154. package/go.mod +22 -0
  155. package/go.sum +39 -0
  156. package/output/output.go +10 -0
  157. package/package.json +14 -0
  158. package/tsconfig.json +10 -0
  159. package/types/tokens.go +65 -0
  160. package/types/types.go +46 -0
@@ -0,0 +1,313 @@
1
+ package compliance
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "fmt" // Added for Sprintf
7
+ "io"
8
+ "os"
9
+ "os/exec"
10
+ "path/filepath"
11
+ "strings"
12
+ "testing"
13
+
14
+ "github.com/paralin/goscript/compiler"
15
+ "github.com/sirupsen/logrus"
16
+ )
17
+
18
+ // TestCase defines a single Go-to-TypeScript compliance test.
19
+ type TestCase struct {
20
+ Name string
21
+ GoSource string
22
+ ExpectedOutput string
23
+ }
24
+
25
+ // PrepareTempTestDir creates a temp dir, copies .go files, and writes go.mod. Returns tempDir path.
26
+ func PrepareTempTestDir(t *testing.T, testDir string) string {
27
+ t.Helper()
28
+ tempDir, err := os.MkdirTemp("", "goscript-test-")
29
+ if err != nil {
30
+ t.Fatalf("failed to create temp dir: %v", err)
31
+ }
32
+
33
+ goModPath := filepath.Join(tempDir, "go.mod")
34
+ goModContent := []byte("module tempmod\n\ngo 1.24\n")
35
+ if err := os.WriteFile(goModPath, goModContent, 0o644); err != nil {
36
+ os.RemoveAll(tempDir) //nolint:errcheck
37
+ t.Fatalf("failed to write go.mod: %v", err)
38
+ }
39
+
40
+ goFiles, err := filepath.Glob(filepath.Join(testDir, "*.go"))
41
+ if err != nil || len(goFiles) == 0 {
42
+ os.RemoveAll(tempDir) //nolint:errcheck
43
+ t.Fatalf("no .go files found in %s", testDir)
44
+ }
45
+ for _, src := range goFiles {
46
+ base := filepath.Base(src)
47
+ dst := filepath.Join(tempDir, base)
48
+ data, err := os.ReadFile(src)
49
+ if err != nil {
50
+ os.RemoveAll(tempDir) //nolint:errcheck
51
+ t.Fatalf("failed to read %s: %v", src, err)
52
+ }
53
+ if err := os.WriteFile(dst, data, 0o644); err != nil {
54
+ os.RemoveAll(tempDir) //nolint:errcheck
55
+ t.Fatalf("failed to write %s: %v", dst, err)
56
+ }
57
+ }
58
+ return tempDir
59
+ }
60
+
61
+ // ReadExpectedLog reads expected.log from testDir.
62
+ func ReadExpectedLog(t *testing.T, testDir string) string {
63
+ t.Helper()
64
+ expectedLogPath := filepath.Join(testDir, "expected.log")
65
+ expected, err := os.ReadFile(expectedLogPath)
66
+ if err != nil {
67
+ t.Fatalf("failed to read expected.log in %s: %v", testDir, err)
68
+ }
69
+ return string(expected)
70
+ }
71
+
72
+ func CompileGoToTypeScript(t *testing.T, testDir, tempDir, outputDir string, le *logrus.Entry) {
73
+ t.Helper()
74
+ conf := &compiler.Config{
75
+ Dir: tempDir,
76
+ OutputPathRoot: outputDir,
77
+ }
78
+ if err := conf.Validate(); err != nil {
79
+ t.Fatalf("invalid compiler config: %v", err)
80
+ }
81
+
82
+ // Log each .go file and its mapped .gs.ts output file and contents
83
+ goFiles, err := filepath.Glob(filepath.Join(tempDir, "*.go"))
84
+ if err != nil || len(goFiles) == 0 {
85
+ t.Fatalf("no .go files found in %s: %v", tempDir, err)
86
+ }
87
+ for _, src := range goFiles {
88
+ base := filepath.Base(src)
89
+ out := filepath.Join(outputDir, compiler.TranslateGoFilePathToTypescriptFilePath("tempmod", base))
90
+ t.Logf("Compiling Go file: %s => %s", src, out)
91
+ if data, err := os.ReadFile(src); err == nil {
92
+ t.Logf("Source %s:\n%s", src, string(data))
93
+ } else {
94
+ t.Logf("could not read source %s: %v", src, err)
95
+ }
96
+ }
97
+
98
+ comp, err := compiler.NewCompiler(conf, le, nil)
99
+ if err != nil {
100
+ t.Fatalf("failed to create compiler: %v", err)
101
+ }
102
+ cmpErr := comp.CompilePackages(context.Background(), ".")
103
+ if cmpErr != nil {
104
+ t.Errorf("compilation failed: %v", err)
105
+ }
106
+
107
+ // Log generated TypeScript files and copy them back to testDir
108
+ if err := filepath.WalkDir(outputDir, func(path string, d os.DirEntry, err error) error {
109
+ if err != nil {
110
+ t.Logf("error walking path %s: %v", path, err)
111
+ return nil
112
+ }
113
+ if strings.HasSuffix(path, ".gs.ts") { // Look specifically for .gs.ts files
114
+ // Determine the destination path in the original testDir
115
+ relPath, err := filepath.Rel(outputDir, path)
116
+ if err != nil {
117
+ t.Logf("failed to get relative path for %s: %v", path, err)
118
+ return nil // Continue walking
119
+ }
120
+ // relPath is like "@go/tempmod/file.gs.ts", so extract the base file name
121
+ parts := strings.Split(relPath, string(filepath.Separator))
122
+ if len(parts) < 3 {
123
+ t.Logf("unexpected path structure for %s", path)
124
+ return nil // Continue walking
125
+ }
126
+ fileName := parts[len(parts)-1]
127
+ destPath := filepath.Join(testDir, fileName)
128
+
129
+ // Read the generated content
130
+ generatedContent, err := os.ReadFile(path)
131
+ if err != nil {
132
+ t.Logf("could not read generated file %s: %v", path, err)
133
+ return nil // Continue walking
134
+ }
135
+
136
+ // Determine the original Go file name
137
+ goFileName := strings.TrimSuffix(fileName, ".gs.ts") + ".go"
138
+
139
+ // Construct the comment
140
+ comment := fmt.Sprintf("// Generated file based on %s\n// Updated when compliance tests are re-run, DO NOT EDIT!\n\n", goFileName)
141
+
142
+ // Prepend the comment to the generated content
143
+ finalContent := append([]byte(comment), generatedContent...)
144
+
145
+ if err := os.WriteFile(destPath, finalContent, 0o644); err != nil {
146
+ t.Logf("failed to write file %s: %v", destPath, err)
147
+ return err
148
+ }
149
+
150
+ t.Logf("generated content written to %s:\n%s", destPath, string(finalContent))
151
+ }
152
+ return nil
153
+ }); err != nil {
154
+ t.Fatalf("error while walking: %v", err.Error())
155
+ }
156
+
157
+ if cmpErr != nil {
158
+ t.Fatalf("compilation failed: %v", cmpErr)
159
+ }
160
+ }
161
+
162
+ // WriteTypeScriptRunner writes a runner.ts file to tempDir.
163
+ func WriteTypeScriptRunner(t *testing.T, tempDir string) string {
164
+ t.Helper()
165
+
166
+ // Find the Go source file in the temp directory
167
+ goFiles, err := filepath.Glob(filepath.Join(tempDir, "*.go"))
168
+ if err != nil || len(goFiles) == 0 {
169
+ t.Fatalf("could not find Go source file in temp dir %s: %v", tempDir, err)
170
+ }
171
+ if len(goFiles) > 1 {
172
+ // For simplicity, assume only one relevant Go file per test case for now.
173
+ t.Logf("warning: found multiple Go files in %s, using the first one: %s", tempDir, goFiles[0])
174
+ }
175
+ goSourceBase := filepath.Base(goFiles[0])
176
+ tsFileName := strings.TrimSuffix(goSourceBase, ".go") + ".gs.ts"
177
+ tsImportPath := fmt.Sprintf("./output/@go/tempmod/%s", tsFileName)
178
+
179
+ tsRunner := filepath.Join(tempDir, "runner.ts")
180
+ // Import the goscript runtime and the main function from the compiled code
181
+ runnerContent := fmt.Sprintf("import { goscript } from \"./goscript\";\nimport { main } from %q;\nmain();\n", tsImportPath) // Use dynamic path
182
+ if err := os.WriteFile(tsRunner, []byte(runnerContent), 0o644); err != nil {
183
+ t.Fatalf("failed to write runner: %v", err)
184
+ }
185
+ return tsRunner
186
+ }
187
+
188
+ // RunTypeScriptRunner runs the runner.ts file using tsx and returns its stdout.
189
+ func RunTypeScriptRunner(t *testing.T, workspaceDir, tempDir, tsRunner string) string {
190
+ t.Helper()
191
+ cmd := exec.Command("tsx", tsRunner)
192
+ cmd.Dir = tempDir
193
+
194
+ // Prepend node_modules/.bin to PATH
195
+ nodeBinDir := filepath.Join(workspaceDir, "node_modules", ".bin")
196
+ currentPath := os.Getenv("PATH")
197
+ newPath := fmt.Sprintf("%s%c%s", nodeBinDir, os.PathListSeparator, currentPath)
198
+ cmd.Env = append(os.Environ(), "PATH="+newPath) // Set the modified PATH
199
+
200
+ var outBuf, errBuf bytes.Buffer
201
+ cmd.Stdout = io.MultiWriter(&outBuf, os.Stderr)
202
+ cmd.Stderr = os.Stderr // Keep stderr going to the test output for debugging
203
+ if err := cmd.Run(); err != nil {
204
+ t.Fatalf("run failed: %v\nstderr: %s", err, errBuf.String())
205
+ }
206
+ return outBuf.String()
207
+ }
208
+
209
+ // copyFile copies a file from src to dst.
210
+ func copyFile(src, dst string) error {
211
+ sourceFile, err := os.Open(src)
212
+ if err != nil {
213
+ return fmt.Errorf("failed to open source file %s: %w", src, err)
214
+ }
215
+ defer sourceFile.Close() //nolint:errcheck
216
+
217
+ // Ensure destination directory exists
218
+ destDir := filepath.Dir(dst)
219
+ if err := os.MkdirAll(destDir, 0o755); err != nil {
220
+ return fmt.Errorf("failed to create destination directory %s: %w", destDir, err)
221
+ }
222
+
223
+ destFile, err := os.Create(dst)
224
+ if err != nil {
225
+ return fmt.Errorf("failed to create destination file %s: %w", dst, err)
226
+ }
227
+ defer destFile.Close() //nolint:errcheck
228
+
229
+ if _, err := io.Copy(destFile, sourceFile); err != nil {
230
+ return fmt.Errorf("failed to copy file content from %s to %s: %w", src, dst, err)
231
+ }
232
+
233
+ // Ensure data is synced to disk
234
+ if err := destFile.Sync(); err != nil {
235
+ return fmt.Errorf("failed to sync destination file %s: %w", dst, err)
236
+ }
237
+
238
+ return nil
239
+ }
240
+
241
+ // RunGoScriptTestDir compiles all .go files in testDir, runs the generated TypeScript, and compares output to expected.log.
242
+ func RunGoScriptTestDir(t *testing.T, workspaceDir, testDir string) {
243
+ t.Helper()
244
+
245
+ // Check for expect-fail file
246
+ expectFailPath := filepath.Join(testDir, "expect-fail")
247
+ if _, err := os.Stat(expectFailPath); err == nil {
248
+ t.Skipf("Skipping test %s: expect-fail file found", filepath.Base(testDir))
249
+ return // Skip the test
250
+ } else if !os.IsNotExist(err) {
251
+ // If there was an error other than "not exists", fail the test
252
+ t.Fatalf("failed to check for expect-fail file in %s: %v", testDir, err)
253
+ }
254
+
255
+ log := logrus.New()
256
+ log.SetLevel(logrus.DebugLevel)
257
+ le := logrus.NewEntry(log)
258
+
259
+ tempDir := PrepareTempTestDir(t, testDir)
260
+ defer os.RemoveAll(tempDir)
261
+
262
+ // Create tsconfig.json in the temporary directory for path aliases
263
+ builtinTsPath := filepath.Join(workspaceDir, "builtin", "builtin.ts") // Use passed workspaceDir
264
+ // Ensure the path uses forward slashes for JSON compatibility, even on Windows
265
+ builtinTsPathForJSON := filepath.ToSlash(builtinTsPath)
266
+ tsconfigContent := fmt.Sprintf(`{
267
+ "compilerOptions": {
268
+ "baseUrl": ".",
269
+ "paths": {
270
+ "@go/builtin": ["%s"]
271
+ }
272
+ }
273
+ }`, builtinTsPathForJSON) // Use dynamic path
274
+ tsconfigPath := filepath.Join(tempDir, "tsconfig.json")
275
+ if err := os.WriteFile(tsconfigPath, []byte(tsconfigContent), 0o644); err != nil {
276
+ t.Fatalf("failed to write tsconfig.json to temp dir: %v", err)
277
+ }
278
+
279
+ outputDir := filepath.Join(tempDir, "output")
280
+ CompileGoToTypeScript(t, testDir, tempDir, outputDir, le) // Pass testDir to enable copying output files back to the test directory
281
+
282
+ // Copy the goscript runtime file to the temp directory
283
+ // Use absolute path to avoid issues with changing working directories
284
+ runtimeSrc := filepath.Join(workspaceDir, "builtin", "builtin.ts") // Use passed workspaceDir
285
+ runtimeDst := filepath.Join(tempDir, "builtin.ts") // Rename to builtin.ts in temp dir
286
+ if err := copyFile(runtimeSrc, runtimeDst); err != nil {
287
+ t.Fatalf("failed to copy goscript runtime file: %v", err)
288
+ }
289
+
290
+ tsRunner := WriteTypeScriptRunner(t, tempDir)
291
+ actual := strings.TrimSpace(RunTypeScriptRunner(t, workspaceDir, tempDir, tsRunner)) // Pass workspaceDir
292
+
293
+ expectedLogPath := filepath.Join(testDir, "expected.log")
294
+ expected, err := os.ReadFile(expectedLogPath)
295
+ if os.IsNotExist(err) {
296
+ // If expected.log doesn't exist, write the actual output to it
297
+ t.Logf("expected.log not found, writing actual output to %s", expectedLogPath)
298
+ if writeErr := os.WriteFile(expectedLogPath, []byte(actual), 0o644); writeErr != nil {
299
+ t.Fatalf("failed to write expected.log: %v", writeErr)
300
+ }
301
+ // Test passes on the first run if expected.log is generated
302
+ t.Logf("Generated expected.log for %s", filepath.Base(testDir))
303
+ } else if err != nil {
304
+ // If there was another error reading expected.log, fail the test
305
+ t.Fatalf("failed to read expected.log in %s: %v", testDir, err)
306
+ } else {
307
+ // If expected.log exists, compare actual output to expected
308
+ exp := strings.TrimSpace(string(expected))
309
+ if actual != exp {
310
+ t.Fatalf("output mismatch\nExpected:\n%s\nActual:\n%s", exp, actual)
311
+ }
312
+ }
313
+ }
@@ -0,0 +1,57 @@
1
+ package compliance
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "sync"
7
+ "testing"
8
+ )
9
+
10
+ func TestCompliance(t *testing.T) {
11
+ testsDir := "./tests"
12
+ dirs, err := os.ReadDir(testsDir)
13
+ if err != nil {
14
+ t.Fatalf("failed to read tests dir: %v", err)
15
+ }
16
+
17
+ // Get workspace directory (project root)
18
+ workspaceDir, err := os.Getwd()
19
+ if err != nil {
20
+ t.Fatalf("failed to get working directory: %v", err)
21
+ }
22
+ workspaceDir = filepath.Join(workspaceDir, "..")
23
+
24
+ // First collect all test paths
25
+ var testPaths []string
26
+ for _, dir := range dirs {
27
+ if !dir.IsDir() {
28
+ continue
29
+ }
30
+ testPath := filepath.Join(testsDir, dir.Name())
31
+ // expectedLogPath := filepath.Join(testPath, "expected.log")
32
+ // if _, err := os.Stat(expectedLogPath); err != nil {
33
+ // continue // skip if no expected.log
34
+ //}
35
+ goFiles, err := filepath.Glob(filepath.Join(testPath, "*.go"))
36
+ if err != nil || len(goFiles) == 0 {
37
+ t.Errorf("no .go files found in %s", testPath)
38
+ continue
39
+ }
40
+ testPaths = append(testPaths, testPath)
41
+ }
42
+
43
+ // Now run tests in parallel with goroutines
44
+ var wg sync.WaitGroup
45
+ for _, testPath := range testPaths {
46
+ wg.Add(1)
47
+ go func(path string) {
48
+ defer wg.Done()
49
+ t.Run(filepath.Base(path), func(t *testing.T) {
50
+ RunGoScriptTestDir(t, workspaceDir, path) // Pass workspaceDir
51
+ })
52
+ }(testPath)
53
+ }
54
+
55
+ // Wait for all tests to complete
56
+ wg.Wait()
57
+ }
@@ -0,0 +1,15 @@
1
+ package main
2
+
3
+ func main() {
4
+ // Test basic array literal
5
+ var a [3]int = [3]int{1, 2, 3}
6
+ println(a[0], a[1], a[2])
7
+
8
+ // Test array literal with inferred length
9
+ b := [...]string{"hello", "world"}
10
+ println(b[0], b[1])
11
+
12
+ // Test array literal with specific element initialization
13
+ c := [5]int{1: 10, 3: 30}
14
+ println(c[0], c[1], c[2], c[3], c[4])
15
+ }
@@ -0,0 +1,19 @@
1
+ // Generated file based on array_literal.go
2
+ // Updated when compliance tests are re-run, DO NOT EDIT!
3
+
4
+ import * as goscript from "@go/builtin";
5
+
6
+ export async function main(): Promise<void> {
7
+ // Test basic array literal
8
+ let a: number[] = [1, 2, 3];
9
+ console.log(a[0], a[1], a[2])
10
+
11
+ // Test array literal with inferred length
12
+ let b = ["hello", "world"]
13
+ console.log(b[0], b[1])
14
+
15
+ // Test array literal with specific element initialization
16
+ let c = [0, 10, 0, 30, 0]
17
+ console.log(c[0], c[1], c[2], c[3], c[4])
18
+ }
19
+
@@ -0,0 +1,3 @@
1
+ 1 2 3
2
+ hello world
3
+ 0 10 0 30 0
@@ -0,0 +1,26 @@
1
+ package main
2
+
3
+ // This function receives from a channel, making it async.
4
+ func receiveFromChan(ch chan int) int {
5
+ val := <-ch // This operation makes the function async
6
+ return val
7
+ }
8
+
9
+ // This function calls an async function, making it async too.
10
+ func caller(ch chan int) int {
11
+ // We expect this call to be awaited in TypeScript
12
+ result := receiveFromChan(ch)
13
+ return result + 1
14
+ }
15
+
16
+ func main() {
17
+ // Create a buffered channel
18
+ myChan := make(chan int, 1)
19
+ myChan <- 10 // Send a value
20
+
21
+ // Call the async caller function
22
+ finalResult := caller(myChan)
23
+ println(finalResult) // Expected output: 11
24
+
25
+ close(myChan)
26
+ }
@@ -0,0 +1,30 @@
1
+ // Generated file based on async_basic.go
2
+ // Updated when compliance tests are re-run, DO NOT EDIT!
3
+
4
+ import * as goscript from "@go/builtin";
5
+
6
+ // This function receives from a channel, making it async.
7
+ async function receiveFromChan(ch: goscript.Channel<number>): Promise<number> {
8
+ let val = await ch.receive() // This operation makes the function async
9
+ return val
10
+ }
11
+
12
+ // This function calls an async function, making it async too.
13
+ async function caller(ch: goscript.Channel<number>): Promise<number> {
14
+ // We expect this call to be awaited in TypeScript
15
+ let result = await receiveFromChan(ch)
16
+ return result + 1
17
+ }
18
+
19
+ export async function main(): Promise<void> {
20
+ // Create a buffered channel
21
+ let myChan = goscript.makeChannel<number>(1)
22
+ await myChan.send(10)
23
+
24
+ // Call the async caller function
25
+ let finalResult = await caller(myChan)
26
+ console.log(finalResult) // Expected output: 11
27
+
28
+ myChan.close()
29
+ }
30
+
@@ -0,0 +1,15 @@
1
+ package main
2
+
3
+ func main() {
4
+ // === Basic Arithmetic ===
5
+ add := 2 + 3
6
+ sub := 10 - 4
7
+ mul := 6 * 7
8
+ div := 20 / 5
9
+ mod := 17 % 3
10
+ println("Addition: Expected: 5, Actual:", add)
11
+ println("Subtraction: Expected: 6, Actual:", sub)
12
+ println("Multiplication: Expected: 42, Actual:", mul)
13
+ println("Division: Expected: 4, Actual:", div)
14
+ println("Modulus: Expected: 2, Actual:", mod)
15
+ }
@@ -0,0 +1,19 @@
1
+ // Generated file based on basic_arithmetic.go
2
+ // Updated when compliance tests are re-run, DO NOT EDIT!
3
+
4
+ import * as goscript from "@go/builtin";
5
+
6
+ export async function main(): Promise<void> {
7
+ // === Basic Arithmetic ===
8
+ let add = 2 + 3
9
+ let sub = 10 - 4
10
+ let mul = 6 * 7
11
+ let div = 20 / 5
12
+ let mod = 17 % 3
13
+ console.log("Addition: Expected: 5, Actual:", add)
14
+ console.log("Subtraction: Expected: 6, Actual:", sub)
15
+ console.log("Multiplication: Expected: 42, Actual:", mul)
16
+ console.log("Division: Expected: 4, Actual:", div)
17
+ console.log("Modulus: Expected: 2, Actual:", mod)
18
+ }
19
+
@@ -0,0 +1,5 @@
1
+ Addition: Expected: 5, Actual: 5
2
+ Subtraction: Expected: 6, Actual: 6
3
+ Multiplication: Expected: 42, Actual: 42
4
+ Division: Expected: 4, Actual: 4
5
+ Modulus: Expected: 2, Actual: 2
@@ -0,0 +1,13 @@
1
+ package main
2
+
3
+ func main() {
4
+ // === Boolean Logic ===
5
+ a := true
6
+ b := false
7
+ and := a && b
8
+ or := a || b
9
+ notA := !a
10
+ println("AND: Expected: false, Actual:", and)
11
+ println("OR: Expected: true, Actual:", or)
12
+ println("NOT: Expected: false, Actual:", notA)
13
+ }
@@ -0,0 +1,17 @@
1
+ // Generated file based on boolean_logic.go
2
+ // Updated when compliance tests are re-run, DO NOT EDIT!
3
+
4
+ import * as goscript from "@go/builtin";
5
+
6
+ export async function main(): Promise<void> {
7
+ // === Boolean Logic ===
8
+ let a = true
9
+ let b = false
10
+ let and = a && b
11
+ let or = a || b
12
+ let notA = !a
13
+ console.log("AND: Expected: false, Actual:", and)
14
+ console.log("OR: Expected: true, Actual:", or)
15
+ console.log("NOT: Expected: false, Actual:", notA)
16
+ }
17
+
@@ -0,0 +1,3 @@
1
+ AND: Expected: false, Actual: false
2
+ OR: Expected: true, Actual: true
3
+ NOT: Expected: false, Actual: false
@@ -0,0 +1,12 @@
1
+ package main
2
+
3
+ func main() {
4
+ messages := make(chan string)
5
+
6
+ go func() {
7
+ messages <- "ping"
8
+ }()
9
+
10
+ msg := <-messages
11
+ println(msg)
12
+ }
@@ -0,0 +1,18 @@
1
+ // Generated file based on channel_basic.go
2
+ // Updated when compliance tests are re-run, DO NOT EDIT!
3
+
4
+ import * as goscript from "@go/builtin";
5
+
6
+ export async function main(): Promise<void> {
7
+ let messages = goscript.makeChannel<string>(0)
8
+
9
+ queueMicrotask(async () => {
10
+ {
11
+ await messages.send("ping")
12
+ }
13
+ })
14
+
15
+ let msg = await messages.receive()
16
+ console.log(msg)
17
+ }
18
+
@@ -0,0 +1,20 @@
1
+ package main
2
+
3
+ type MyStruct struct {
4
+ MyInt int
5
+ MyString string
6
+ myBool bool
7
+ }
8
+
9
+ func main() {
10
+ // === Composite Literal Assignment (Value Copy) ===
11
+ // Creating a struct directly using a composite literal.
12
+ structLiteral := MyStruct{MyString: "composite literal"}
13
+ // Assigning it creates another independent copy.
14
+ structLiteralCopy := structLiteral
15
+ structLiteralCopy.MyString = "modified composite literal copy"
16
+ // Expected: "composite literal"
17
+ println("Original struct literal: Expected: composite literal, Actual: " + structLiteral.MyString)
18
+ // Expected: "modified composite literal copy"
19
+ println("Modified struct literal copy: Expected: modified composite literal copy, Actual: " + structLiteralCopy.MyString)
20
+ }
@@ -0,0 +1,27 @@
1
+ // Generated file based on composite_literal_assignment.go
2
+ // Updated when compliance tests are re-run, DO NOT EDIT!
3
+
4
+ import * as goscript from "@go/builtin";
5
+
6
+ class MyStruct {
7
+ public MyInt: number = 0;
8
+ public MyString: string = "";
9
+ private myBool: boolean = false;
10
+
11
+ constructor(init?: Partial<MyStruct>) { if (init) Object.assign(this, init as any); }
12
+ public clone(): MyStruct { return Object.assign(Object.create(MyStruct.prototype) as MyStruct, this); }
13
+ }
14
+
15
+ export async function main(): Promise<void> {
16
+ // === Composite Literal Assignment (Value Copy) ===
17
+ // Creating a struct directly using a composite literal.
18
+ let structLiteral = new MyStruct({ MyString: "composite literal" })
19
+ // Assigning it creates another independent copy.
20
+ let structLiteralCopy = structLiteral.clone()
21
+ structLiteralCopy.MyString = "modified composite literal copy"
22
+ // Expected: "composite literal"
23
+ console.log("Original struct literal: Expected: composite literal, Actual: " + structLiteral.MyString)
24
+ // Expected: "modified composite literal copy"
25
+ console.log("Modified struct literal copy: Expected: modified composite literal copy, Actual: " + structLiteralCopy.MyString)
26
+ }
27
+
@@ -0,0 +1,2 @@
1
+ Original struct literal: Expected: composite literal, Actual: composite literal
2
+ Modified struct literal copy: Expected: modified composite literal copy, Actual: modified composite literal copy
@@ -0,0 +1,18 @@
1
+ package main
2
+
3
+ const (
4
+ Pi = 3.14
5
+ Truth = false
6
+ // TODO: Handle large integer constants and bit shifts exceeding JS number limits.
7
+ // Big = 1 << 60
8
+ // Small = Big >> 59 // Commented out as it depends on Big
9
+ Greeting = "Hello, Constants!"
10
+ )
11
+
12
+ func main() {
13
+ println(Pi)
14
+ println(Truth)
15
+ // println(Big) // Commented out until large integer handling is implemented
16
+ // println(Small) // Commented out as it depends on Big
17
+ println(Greeting)
18
+ }