goscript 0.0.22 → 0.0.24

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 (66) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +3 -3
  3. package/compiler/analysis.go +302 -182
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +42 -43
  6. package/compiler/builtin_test.go +102 -0
  7. package/compiler/compiler.go +117 -29
  8. package/compiler/compiler_test.go +36 -8
  9. package/compiler/composite-lit.go +133 -53
  10. package/compiler/config.go +7 -3
  11. package/compiler/config_test.go +6 -33
  12. package/compiler/decl.go +36 -0
  13. package/compiler/expr-call.go +116 -60
  14. package/compiler/expr-selector.go +88 -43
  15. package/compiler/expr-star.go +57 -65
  16. package/compiler/expr-type.go +132 -5
  17. package/compiler/expr-value.go +8 -38
  18. package/compiler/expr.go +326 -30
  19. package/compiler/field.go +3 -3
  20. package/compiler/lit.go +34 -2
  21. package/compiler/primitive.go +19 -12
  22. package/compiler/spec-struct.go +140 -9
  23. package/compiler/spec-value.go +119 -41
  24. package/compiler/spec.go +21 -6
  25. package/compiler/stmt-assign.go +65 -3
  26. package/compiler/stmt-for.go +11 -0
  27. package/compiler/stmt-range.go +119 -11
  28. package/compiler/stmt-select.go +211 -0
  29. package/compiler/stmt-type-switch.go +147 -0
  30. package/compiler/stmt.go +175 -238
  31. package/compiler/type-assert.go +125 -379
  32. package/compiler/type.go +216 -129
  33. package/dist/gs/builtin/builtin.js +37 -0
  34. package/dist/gs/builtin/builtin.js.map +1 -0
  35. package/dist/gs/builtin/channel.js +471 -0
  36. package/dist/gs/builtin/channel.js.map +1 -0
  37. package/dist/gs/builtin/defer.js +54 -0
  38. package/dist/gs/builtin/defer.js.map +1 -0
  39. package/dist/gs/builtin/io.js +15 -0
  40. package/dist/gs/builtin/io.js.map +1 -0
  41. package/dist/gs/builtin/map.js +44 -0
  42. package/dist/gs/builtin/map.js.map +1 -0
  43. package/dist/gs/builtin/slice.js +799 -0
  44. package/dist/gs/builtin/slice.js.map +1 -0
  45. package/dist/gs/builtin/type.js +745 -0
  46. package/dist/gs/builtin/type.js.map +1 -0
  47. package/dist/gs/builtin/varRef.js +14 -0
  48. package/dist/gs/builtin/varRef.js.map +1 -0
  49. package/dist/gs/context/context.js +55 -0
  50. package/dist/gs/context/context.js.map +1 -0
  51. package/dist/gs/context/index.js +2 -0
  52. package/dist/gs/context/index.js.map +1 -0
  53. package/dist/gs/runtime/index.js +2 -0
  54. package/dist/gs/runtime/index.js.map +1 -0
  55. package/dist/gs/runtime/runtime.js +158 -0
  56. package/dist/gs/runtime/runtime.js.map +1 -0
  57. package/dist/gs/time/index.js +2 -0
  58. package/dist/gs/time/index.js.map +1 -0
  59. package/dist/gs/time/time.js +115 -0
  60. package/dist/gs/time/time.js.map +1 -0
  61. package/package.json +7 -6
  62. package/builtin/builtin.go +0 -11
  63. package/builtin/builtin.ts +0 -2379
  64. package/dist/builtin/builtin.d.ts +0 -513
  65. package/dist/builtin/builtin.js +0 -1686
  66. package/dist/builtin/builtin.js.map +0 -1
@@ -0,0 +1,220 @@
1
+ package compiler
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/parser"
6
+ "go/token"
7
+ "go/types"
8
+ "testing"
9
+
10
+ "golang.org/x/tools/go/packages"
11
+ )
12
+
13
+ // TestAnalysisVarRefLogic verifies that the analysis correctly identifies
14
+ // which variables need variable references based on actual compliance test cases
15
+ func TestAnalysisVarRefLogic(t *testing.T) {
16
+ tests := []struct {
17
+ name string
18
+ code string
19
+ expected map[string]AnalysisExpectation
20
+ }{
21
+ {
22
+ name: "pointer_range_loop",
23
+ code: `package main
24
+ func main() {
25
+ arr := [3]int{1, 2, 3}
26
+ arrPtr := &arr
27
+ for i, v := range arrPtr {
28
+ println("index:", i, "value:", v)
29
+ }
30
+ }`,
31
+ expected: map[string]AnalysisExpectation{
32
+ "arr": {NeedsVarRef: true, NeedsVarRefAccess: true}, // varrefed because &arr is taken
33
+ "arrPtr": {NeedsVarRef: false, NeedsVarRefAccess: true}, // NOT varrefed, but points to varrefed value
34
+ "i": {NeedsVarRef: false, NeedsVarRefAccess: false}, // regular loop variable
35
+ "v": {NeedsVarRef: false, NeedsVarRefAccess: false}, // regular loop variable
36
+ },
37
+ },
38
+ {
39
+ name: "simple_pointers",
40
+ code: `package main
41
+ type MyStruct struct {
42
+ Val int
43
+ }
44
+ func main() {
45
+ s1 := MyStruct{Val: 1}
46
+ p1 := &s1
47
+ pp1 := &p1
48
+ p4 := &s1
49
+ _ = p4
50
+ _ = pp1
51
+ }`,
52
+ expected: map[string]AnalysisExpectation{
53
+ "s1": {NeedsVarRef: true, NeedsVarRefAccess: true}, // varrefed because &s1 is taken
54
+ "p1": {NeedsVarRef: true, NeedsVarRefAccess: true}, // varrefed because &p1 is taken
55
+ "pp1": {NeedsVarRef: false, NeedsVarRefAccess: true}, // NOT varrefed, points to varrefed value
56
+ "p4": {NeedsVarRef: false, NeedsVarRefAccess: true}, // NOT varrefed, points to varrefed value
57
+ },
58
+ },
59
+ {
60
+ name: "varref_deref_struct",
61
+ code: `package main
62
+ type MyStruct struct {
63
+ MyInt int
64
+ }
65
+ func main() {
66
+ myStruct := &MyStruct{}
67
+ (*myStruct).MyInt = 5
68
+ println((*myStruct).MyInt)
69
+ }`,
70
+ expected: map[string]AnalysisExpectation{
71
+ "myStruct": {NeedsVarRef: false, NeedsVarRefAccess: false}, // NOT varrefed, direct pointer to struct
72
+ },
73
+ },
74
+ {
75
+ name: "pointer_composite_literal_untyped",
76
+ code: `package main
77
+ func main() {
78
+ var ptr *struct{ x int }
79
+ ptr = &struct{ x int }{42}
80
+ println("Pointer value x:", ptr.x)
81
+
82
+ data := []*struct{ x int }{{42}, {43}}
83
+ println("First element x:", data[0].x)
84
+ println("Second element x:", data[1].x)
85
+ }`,
86
+ expected: map[string]AnalysisExpectation{
87
+ "ptr": {NeedsVarRef: false, NeedsVarRefAccess: false}, // Should NOT be varrefed
88
+ "data": {NeedsVarRef: false, NeedsVarRefAccess: false}, // Should NOT be varrefed
89
+ },
90
+ },
91
+ }
92
+
93
+ for _, tt := range tests {
94
+ t.Run(tt.name, func(t *testing.T) {
95
+ analysis, objects := parseAndAnalyze(t, tt.code)
96
+
97
+ for varName, expected := range tt.expected {
98
+ obj, exists := objects[varName]
99
+ if !exists {
100
+ t.Errorf("Variable %q not found in parsed objects", varName)
101
+ continue
102
+ }
103
+
104
+ actualNeedsVarRef := analysis.NeedsVarRef(obj)
105
+ actualNeedsVarRefAccess := analysis.NeedsVarRefAccess(obj)
106
+
107
+ if actualNeedsVarRef != expected.NeedsVarRef {
108
+ t.Errorf("Variable %q: NeedsVarRef = %v, want %v",
109
+ varName, actualNeedsVarRef, expected.NeedsVarRef)
110
+ }
111
+
112
+ if actualNeedsVarRefAccess != expected.NeedsVarRefAccess {
113
+ t.Errorf("Variable %q: NeedsVarRefAccess = %v, want %v",
114
+ varName, actualNeedsVarRefAccess, expected.NeedsVarRefAccess)
115
+ }
116
+
117
+ // Print debug info
118
+ t.Logf("Variable %q: NeedsVarRef=%v, NeedsVarRefAccess=%v",
119
+ varName, actualNeedsVarRef, actualNeedsVarRefAccess)
120
+ }
121
+ })
122
+ }
123
+ }
124
+
125
+ // AnalysisExpectation defines what we expect from the analysis for a variable
126
+ type AnalysisExpectation struct {
127
+ NeedsVarRef bool
128
+ NeedsVarRefAccess bool
129
+ }
130
+
131
+ // parseAndAnalyze parses Go code and runs the analysis, returning the analysis and variable objects
132
+ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Object) {
133
+ // Parse the code
134
+ fset := token.NewFileSet()
135
+ file, err := parser.ParseFile(fset, "test.go", code, parser.ParseComments)
136
+ if err != nil {
137
+ t.Fatalf("Failed to parse code: %v", err)
138
+ }
139
+
140
+ // Create a minimal package for type checking
141
+ pkg := &packages.Package{
142
+ Syntax: []*ast.File{file},
143
+ TypesInfo: &types.Info{
144
+ Types: make(map[ast.Expr]types.TypeAndValue),
145
+ Defs: make(map[*ast.Ident]types.Object),
146
+ Uses: make(map[*ast.Ident]types.Object),
147
+ },
148
+ }
149
+
150
+ // Type check the package
151
+ typeConfig := &types.Config{}
152
+ typePkg, err := typeConfig.Check("main", fset, pkg.Syntax, pkg.TypesInfo)
153
+ if err != nil {
154
+ t.Fatalf("Failed to type check: %v", err)
155
+ }
156
+ pkg.Types = typePkg
157
+
158
+ // Run analysis
159
+ analysis := NewAnalysis()
160
+ cmap := ast.NewCommentMap(fset, file, file.Comments)
161
+ AnalyzeFile(file, pkg, analysis, cmap)
162
+
163
+ // Collect variable objects
164
+ objects := make(map[string]types.Object)
165
+ for ident, obj := range pkg.TypesInfo.Defs {
166
+ if obj != nil && ident.Name != "_" {
167
+ if _, isVar := obj.(*types.Var); isVar {
168
+ objects[ident.Name] = obj
169
+ }
170
+ }
171
+ }
172
+
173
+ return analysis, objects
174
+ }
175
+
176
+ // TestAnalysisDebugInfo prints debug information about variable usage for manual inspection
177
+ func TestAnalysisDebugInfo(t *testing.T) {
178
+ code := `package main
179
+ func main() {
180
+ arr := [3]int{1, 2, 3}
181
+ arrPtr := &arr
182
+ for i, v := range arrPtr {
183
+ println("index:", i, "value:", v)
184
+ }
185
+ }`
186
+
187
+ analysis, objects := parseAndAnalyze(t, code)
188
+
189
+ t.Log("=== Analysis Debug Information ===")
190
+ for varName, obj := range objects {
191
+ needsVarRef := analysis.NeedsVarRef(obj)
192
+ needsVarRefAccess := analysis.NeedsVarRefAccess(obj)
193
+
194
+ t.Logf("Variable: %s", varName)
195
+ t.Logf(" Type: %s", obj.Type())
196
+ t.Logf(" NeedsVarRef: %v", needsVarRef)
197
+ t.Logf(" NeedsVarRefAccess: %v", needsVarRefAccess)
198
+
199
+ // Print usage info if available
200
+ if usage, exists := analysis.VariableUsage[obj]; exists {
201
+ t.Logf(" Sources: %d", len(usage.Sources))
202
+ for i, src := range usage.Sources {
203
+ srcName := "nil"
204
+ if src.Object != nil {
205
+ srcName = src.Object.Name()
206
+ }
207
+ t.Logf(" [%d] %s (type: %v)", i, srcName, src.Type)
208
+ }
209
+ t.Logf(" Destinations: %d", len(usage.Destinations))
210
+ for i, dst := range usage.Destinations {
211
+ dstName := "nil"
212
+ if dst.Object != nil {
213
+ dstName = dst.Object.Name()
214
+ }
215
+ t.Logf(" [%d] %s (type: %v)", i, dstName, dst.Type)
216
+ }
217
+ }
218
+ t.Log("")
219
+ }
220
+ }
@@ -21,7 +21,7 @@ import (
21
21
  // to `$.mapSet(myMap_ts, key_ts, value_ts)`.
22
22
  // - Other single-variable assignments (`variable = value`):
23
23
  // - The LHS expression is written (caller typically ensures `.value` is appended
24
- // if assigning to a boxed variable's content).
24
+ // if assigning to a VarRefed variable's content).
25
25
  // - The Go assignment token (`tok`, e.g., `=`, `+=`) is translated to its
26
26
  // TypeScript equivalent using `TokenToTs`.
27
27
  // - The RHS expression(s) are written. If `shouldApplyClone` indicates the RHS
@@ -68,10 +68,10 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
68
68
  return nil
69
69
  }
70
70
 
71
- // Special handling for boxed variables in declarations
71
+ // Special handling for variable referenced variables in declarations
72
72
  if addDeclaration && tok == token.DEFINE {
73
- // Determine if LHS is boxed
74
- isLHSBoxed := false
73
+ // Determine if LHS is variable referenced
74
+ isLHSVarRefed := false
75
75
  var lhsIdent *ast.Ident
76
76
  var lhsObj types.Object
77
77
 
@@ -84,30 +84,22 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
84
84
  lhsObj = def
85
85
  }
86
86
 
87
- // Check if this variable needs to be boxed
88
- if lhsObj != nil && c.analysis.NeedsBoxed(lhsObj) {
89
- isLHSBoxed = true
87
+ // Check if this variable needs to be variable referenced
88
+ if lhsObj != nil && c.analysis.NeedsVarRef(lhsObj) {
89
+ isLHSVarRefed = true
90
90
  }
91
91
  }
92
92
 
93
- // Special handling for short declaration of boxed variables
94
- if isLHSBoxed && lhsIdent != nil {
93
+ // Special handling for short declaration of variable referenced variables
94
+ if isLHSVarRefed && lhsIdent != nil {
95
95
  c.tsw.WriteLiterally("let ")
96
96
  // Just write the identifier name without .value
97
97
  c.tsw.WriteLiterally(lhsIdent.Name)
98
-
99
- // Add type annotation for boxed variables in declarations
100
- if lhsObj != nil {
101
- c.tsw.WriteLiterally(": ")
102
- c.tsw.WriteLiterally("$.Box<")
103
- c.WriteGoType(lhsObj.Type())
104
- c.tsw.WriteLiterally(">")
105
- }
106
-
98
+ // No type annotation, allow TypeScript to infer it from varRef.
107
99
  c.tsw.WriteLiterally(" = ")
108
100
 
109
- // Box the initializer
110
- c.tsw.WriteLiterally("$.box(")
101
+ // Create the variable reference for the initializer
102
+ c.tsw.WriteLiterally("$.varRef(")
111
103
  if err := c.WriteValueExpr(rhs[0]); err != nil {
112
104
  return err
113
105
  }
@@ -177,6 +169,11 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
177
169
  return err
178
170
  }
179
171
  c.tsw.WriteLiterally("]")
172
+ } else if callExpr, isCallExpr := r.(*ast.CallExpr); isCallExpr {
173
+ // If the RHS is a function call, write it as a call
174
+ if err := c.WriteCallExpr(callExpr); err != nil {
175
+ return err
176
+ }
180
177
  } else {
181
178
  // Normal case - write the entire expression
182
179
  if err := c.WriteValueExpr(r); err != nil {
@@ -221,12 +218,12 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
221
218
  }
222
219
 
223
220
  if !currentIsMapIndex {
224
- // For single assignments, handle boxed variables specially
221
+ // For single assignments, handle variable referenced variables specially
225
222
  if len(lhs) == 1 && len(rhs) == 1 {
226
223
  lhsExprIdent, lhsExprIsIdent := l.(*ast.Ident)
227
224
  if lhsExprIsIdent {
228
- // Determine if LHS is boxed
229
- isLHSBoxed := false
225
+ // Determine if LHS is variable referenced
226
+ isLHSVarRefed := false
230
227
  var lhsObj types.Object
231
228
 
232
229
  // Get the types.Object from the identifier
@@ -236,13 +233,13 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
236
233
  lhsObj = def
237
234
  }
238
235
 
239
- // Check if this variable needs to be boxed
240
- if lhsObj != nil && c.analysis.NeedsBoxed(lhsObj) {
241
- isLHSBoxed = true
236
+ // Check if this variable needs to be variable referenced
237
+ if lhsObj != nil && c.analysis.NeedsVarRef(lhsObj) {
238
+ isLHSVarRefed = true
242
239
  }
243
240
 
244
- // prevent writing .value unless lhs is boxed
245
- c.WriteIdent(lhsExprIdent, isLHSBoxed)
241
+ // prevent writing .value unless lhs is variable referenced
242
+ c.WriteIdent(lhsExprIdent, isLHSVarRefed)
246
243
  continue
247
244
  }
248
245
  }
@@ -274,15 +271,16 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
274
271
  if i != 0 {
275
272
  c.tsw.WriteLiterally(", ")
276
273
  }
277
- // Check if we need to access a boxed source value and apply clone
274
+
275
+ // Check if we need to access a variable referenced source value and apply clone
278
276
  // For struct value assignments, we need to handle:
279
- // 1. Unboxed source, unboxed target: source.clone()
280
- // 2. Boxed source, unboxed target: source.value.clone()
281
- // 3. Unboxed source, boxed target: $.box(source)
282
- // 4. Boxed source, boxed target: source (straight assignment of the box)
277
+ // 1. UnVarRefed source, unVarRefed target: source.clone()
278
+ // 2. Variable referenced source, unVarRefed target: source.value.clone()
279
+ // 3. UnVarRefed source, variable referenced target: $.varRef(source)
280
+ // 4. Variable referenced source, variable referenced target: source (straight assignment of the variable reference)
283
281
 
284
- // Determine if RHS is a boxed variable (could be a struct or other variable)
285
- needsBoxedAccessRHS := false
282
+ // Determine if RHS is a variable referenced variable (could be a struct or other variable)
283
+ needsVarRefedAccessRHS := false
286
284
  var rhsObj types.Object
287
285
 
288
286
  // Check if RHS is an identifier (variable name)
@@ -293,17 +291,17 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
293
291
  rhsObj = c.pkg.TypesInfo.Defs[rhsIdent]
294
292
  }
295
293
 
296
- // Important: For struct copying, we need to check if the variable itself is boxed
297
- // Important: For struct copying, we need to check if the variable needs boxed access
298
- // This is more comprehensive than just checking if it's boxed
294
+ // Important: For struct copying, we need to check if the variable itself is variable referenced
295
+ // Important: For struct copying, we need to check if the variable needs variable referenced access
296
+ // This is more comprehensive than just checking if it's variable referenced
299
297
  if rhsObj != nil {
300
- needsBoxedAccessRHS = c.analysis.NeedsBoxedAccess(rhsObj)
298
+ needsVarRefedAccessRHS = c.analysis.NeedsVarRefAccess(rhsObj)
301
299
  }
302
300
  }
303
301
 
304
302
  // Handle different cases for struct cloning
305
303
  if shouldApplyClone(c.pkg, r) {
306
- // For other expressions, we need to handle boxed access differently
304
+ // For other expressions, we need to handle variable referenced access differently
307
305
  if _, isIdent := r.(*ast.Ident); isIdent {
308
306
  // For identifiers, WriteValueExpr already adds .value if needed
309
307
  if err := c.WriteValueExpr(r); err != nil {
@@ -314,14 +312,15 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
314
312
  if err := c.WriteValueExpr(r); err != nil {
315
313
  return err
316
314
  }
317
- // Only add .value for non-identifiers that need boxed access
318
- if needsBoxedAccessRHS {
319
- c.tsw.WriteLiterally(".value") // Access the boxed value
315
+ // Only add .value for non-identifiers that need variable referenced access
316
+ if needsVarRefedAccessRHS {
317
+ c.tsw.WriteLiterally(".value") // Access the variable referenced value
320
318
  }
321
319
  }
322
320
 
323
321
  c.tsw.WriteLiterally(".clone()") // Always add clone for struct values
324
322
  } else {
323
+ // Non-struct case: write RHS normally
325
324
  if err := c.WriteValueExpr(r); err != nil { // RHS is a non-struct value
326
325
  return err
327
326
  }
@@ -0,0 +1,102 @@
1
+ package compiler
2
+
3
+ import (
4
+ "context"
5
+ "os"
6
+ "path/filepath"
7
+ "testing"
8
+
9
+ "github.com/sirupsen/logrus"
10
+ )
11
+
12
+ func TestEmitBuiltinOption(t *testing.T) {
13
+ // Create a temporary directory for the test output
14
+ tempDir, err := os.MkdirTemp("", "goscript-test-emit-builtin")
15
+ if err != nil {
16
+ t.Fatalf("Failed to create temp dir: %v", err)
17
+ }
18
+ defer os.RemoveAll(tempDir)
19
+
20
+ // Setup logger
21
+ log := logrus.New()
22
+ log.SetLevel(logrus.DebugLevel)
23
+ le := logrus.NewEntry(log)
24
+
25
+ // Case 1: DisableEmitBuiltin = true (default behavior in compliance tests)
26
+ t.Run("DisableEmitBuiltin=true", func(t *testing.T) {
27
+ outputDir := filepath.Join(tempDir, "disabled")
28
+ config := &Config{
29
+ OutputPath: outputDir,
30
+ AllDependencies: true,
31
+ DisableEmitBuiltin: true,
32
+ }
33
+
34
+ compiler, err := NewCompiler(config, le, nil)
35
+ if err != nil {
36
+ t.Fatalf("Failed to create compiler: %v", err)
37
+ }
38
+
39
+ // Compile a package that depends on builtin (time)
40
+ err = compiler.CompilePackages(context.Background(), "time")
41
+ if err != nil {
42
+ t.Fatalf("Compilation failed: %v", err)
43
+ }
44
+
45
+ // Check that the unsafe package wasn't emitted (we know it's handwritten)
46
+ unsafePath := filepath.Join(outputDir, "@goscript/unsafe")
47
+ if _, err := os.Stat(unsafePath); !os.IsNotExist(err) {
48
+ t.Errorf("unsafe package was emitted when DisableEmitBuiltin=true")
49
+ }
50
+
51
+ // Also check for runtime package
52
+ runtimePath := filepath.Join(outputDir, "@goscript/runtime")
53
+ if _, err := os.Stat(runtimePath); !os.IsNotExist(err) {
54
+ t.Errorf("runtime package was emitted when DisableEmitBuiltin=true")
55
+ }
56
+
57
+ // But time package should have been emitted
58
+ timePath := filepath.Join(outputDir, "@goscript/time")
59
+ if _, err := os.Stat(timePath); os.IsNotExist(err) {
60
+ t.Errorf("time package was not emitted")
61
+ }
62
+ })
63
+
64
+ // Case 2: DisableEmitBuiltin = false (new behavior for third-party projects)
65
+ t.Run("DisableEmitBuiltin=false", func(t *testing.T) {
66
+ outputDir := filepath.Join(tempDir, "enabled")
67
+ config := &Config{
68
+ OutputPath: outputDir,
69
+ AllDependencies: true,
70
+ DisableEmitBuiltin: false,
71
+ }
72
+
73
+ compiler, err := NewCompiler(config, le, nil)
74
+ if err != nil {
75
+ t.Fatalf("Failed to create compiler: %v", err)
76
+ }
77
+
78
+ // Compile a package that depends on builtin (time)
79
+ err = compiler.CompilePackages(context.Background(), "time")
80
+ if err != nil {
81
+ t.Fatalf("Compilation failed: %v", err)
82
+ }
83
+
84
+ // Check that the unsafe package was copied to the output
85
+ unsafePath := filepath.Join(outputDir, "@goscript/unsafe")
86
+ if _, err := os.Stat(unsafePath); os.IsNotExist(err) {
87
+ t.Errorf("unsafe package was not emitted when DisableEmitBuiltin=false")
88
+ }
89
+
90
+ // Also check for runtime package
91
+ runtimePath := filepath.Join(outputDir, "@goscript/runtime")
92
+ if _, err := os.Stat(runtimePath); os.IsNotExist(err) {
93
+ t.Errorf("runtime package was not emitted when DisableEmitBuiltin=false")
94
+ }
95
+
96
+ // Time package should also have been emitted
97
+ timePath := filepath.Join(outputDir, "@goscript/time")
98
+ if _, err := os.Stat(timePath); os.IsNotExist(err) {
99
+ t.Errorf("time package was not emitted")
100
+ }
101
+ })
102
+ }