goscript 0.0.21 → 0.0.22

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.
@@ -5,6 +5,7 @@ import (
5
5
  "os/exec"
6
6
  "path/filepath"
7
7
  "runtime"
8
+ "slices"
8
9
  "sync"
9
10
  "sync/atomic"
10
11
  "testing"
@@ -43,6 +44,9 @@ func TestCompliance(t *testing.T) {
43
44
  testPaths = append(testPaths, testPath)
44
45
  }
45
46
 
47
+ // sort testPaths
48
+ slices.Sort(testPaths)
49
+
46
50
  // limit concurrency
47
51
  simulLimit := make(chan struct{}, runtime.GOMAXPROCS(-1)*2)
48
52
  for range cap(simulLimit) {
@@ -0,0 +1,537 @@
1
+ package compiler
2
+
3
+ import (
4
+ "fmt"
5
+ "go/ast"
6
+ "go/token"
7
+ "go/types"
8
+ "slices"
9
+ )
10
+
11
+ // WriteCompositeLit translates a Go composite literal (ast.CompositeLit) into its
12
+ // TypeScript equivalent.
13
+ //
14
+ // It handles several types of composite literals:
15
+ // - Map literals (e.g., `map[K]V{k1: v1}`): Translated to `new Map([[k1_ts, v1_ts]])`.
16
+ // Values are processed by `writeBoxedValue`.
17
+ // - Array/Slice literals (e.g., `[]T{e1, e2}`, `[N]T{idx: val}`):
18
+ // Translated using the `$.arrayToSlice<T_ts>([...])` runtime helper.
19
+ // It handles both keyed and unkeyed elements, infers length if necessary,
20
+ // and uses zero values for uninitialized array elements.
21
+ // Multi-dimensional arrays/slices pass a depth parameter to `$.arrayToSlice`.
22
+ // Element values are processed by `writeBoxedValue`.
23
+ // - Struct literals:
24
+ // - Named structs (e.g., `MyStruct{F: v}` or `&MyStruct{F: v}`): Translated to
25
+ // `new MyStruct_ts({ F: v_ts, ... })`. The constructor typically uses an `_init` method.
26
+ // - Anonymous structs (e.g., `struct{F int}{F: v}`): Translated to TypeScript
27
+ // object literals `{ F: v_ts, ... }`.
28
+ // It processes keyed elements (`FieldName: Value`) and unkeyed elements (for anonymous
29
+ // structs or arrays). Field values are processed by `writeBoxedValue`.
30
+ // Embedded struct fields are initialized, and explicit initializers for embedded
31
+ // structs (e.g. `Outer{InnerField: InnerType{...}}`) are handled.
32
+ // The function uses `c.analysis` to determine correct value access (e.g., `.value` for boxed fields).
33
+ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
34
+ // Get the type of the composite literal
35
+ litType := c.pkg.TypesInfo.TypeOf(exp)
36
+ if exp.Type != nil {
37
+ // Handle map literals: map[K]V{k1: v1, k2: v2}
38
+ if _, isMapType := exp.Type.(*ast.MapType); isMapType {
39
+ c.tsw.WriteLiterally("new Map([")
40
+
41
+ // Add each key-value pair as an entry
42
+ for i, elm := range exp.Elts {
43
+ if i > 0 {
44
+ c.tsw.WriteLiterally(", ")
45
+ }
46
+
47
+ if kv, ok := elm.(*ast.KeyValueExpr); ok {
48
+ c.tsw.WriteLiterally("[")
49
+ if err := c.WriteBoxedValue(kv.Key); err != nil {
50
+ return fmt.Errorf("failed to write map literal key: %w", err)
51
+ }
52
+ c.tsw.WriteLiterally(", ")
53
+ if err := c.WriteBoxedValue(kv.Value); err != nil {
54
+ return fmt.Errorf("failed to write map literal value: %w", err)
55
+ }
56
+ c.tsw.WriteLiterally("]")
57
+ } else {
58
+ return fmt.Errorf("map literal elements must be key-value pairs")
59
+ }
60
+ }
61
+
62
+ c.tsw.WriteLiterally("])")
63
+ return nil
64
+ }
65
+
66
+ // Handle array literals
67
+ if arrType, isArrayType := exp.Type.(*ast.ArrayType); isArrayType {
68
+ // Check if this is a slice of slices (multi-dimensional array)
69
+ isMultiDimensional := false
70
+ if _, ok := arrType.Elt.(*ast.ArrayType); ok {
71
+ // It's a slice of slices (multi-dimensional array)
72
+ isMultiDimensional = true
73
+ // We'll handle this with depth parameter to arrayToSlice
74
+ }
75
+
76
+ c.tsw.WriteLiterally("$.arrayToSlice")
77
+
78
+ // write the type annotation
79
+ c.tsw.WriteLiterally("<")
80
+ // Write the element type using the existing function
81
+ c.WriteTypeExpr(arrType.Elt)
82
+ c.tsw.WriteLiterally(">")
83
+
84
+ c.tsw.WriteLiterally("([")
85
+
86
+ // Use type info to get array length and element type
87
+ var arrayLen int
88
+ var elemType ast.Expr
89
+ var goElemType interface{}
90
+ if typ := c.pkg.TypesInfo.TypeOf(exp.Type); typ != nil {
91
+ if at, ok := typ.Underlying().(*types.Array); ok {
92
+ arrayLen = int(at.Len())
93
+ goElemType = at.Elem()
94
+ }
95
+ }
96
+ if arrType.Len != nil {
97
+ // Try to evaluate the length from the AST if not available from type info
98
+ if bl, ok := arrType.Len.(*ast.BasicLit); ok && bl.Kind == token.INT {
99
+ if _, err := fmt.Sscan(bl.Value, &arrayLen); err != nil {
100
+ return fmt.Errorf("failed to parse array length from basic literal: %w", err)
101
+ }
102
+ }
103
+ }
104
+ elemType = arrType.Elt
105
+
106
+ // Map of index -> value
107
+ elements := make(map[int]ast.Expr)
108
+ orderedCount := 0
109
+ maxIndex := -1
110
+ hasKeyedElements := false
111
+
112
+ for _, elm := range exp.Elts {
113
+ if kv, ok := elm.(*ast.KeyValueExpr); ok {
114
+ if lit, ok := kv.Key.(*ast.BasicLit); ok && lit.Kind == token.INT {
115
+ var index int
116
+ if _, err := fmt.Sscan(lit.Value, &index); err != nil {
117
+ return fmt.Errorf("failed to parse array index from basic literal: %w", err)
118
+ }
119
+ elements[index] = kv.Value
120
+ if index > maxIndex {
121
+ maxIndex = index
122
+ }
123
+ hasKeyedElements = true
124
+ } else {
125
+ c.tsw.WriteCommentInline("unhandled keyed array literal key type")
126
+ if err := c.WriteBoxedValue(elm); err != nil {
127
+ return fmt.Errorf("failed to write keyed array literal element with unhandled key type: %w", err)
128
+ }
129
+ }
130
+ } else {
131
+ elements[orderedCount] = elm
132
+ if orderedCount > maxIndex {
133
+ maxIndex = orderedCount
134
+ }
135
+ orderedCount++
136
+ }
137
+ }
138
+
139
+ // Determine array length
140
+ if arrayLen == 0 {
141
+ // If length is not set, infer from max index or number of elements
142
+ if hasKeyedElements {
143
+ arrayLen = maxIndex + 1
144
+ } else {
145
+ arrayLen = len(exp.Elts)
146
+ }
147
+ }
148
+
149
+ for i := 0; i < arrayLen; i++ {
150
+ if i > 0 {
151
+ c.tsw.WriteLiterally(", ")
152
+ }
153
+ if elm, ok := elements[i]; ok && elm != nil {
154
+ if err := c.WriteBoxedValue(elm); err != nil {
155
+ return fmt.Errorf("failed to write array literal element: %w", err)
156
+ }
157
+ } else {
158
+ // Write zero value for element type
159
+ if goElemType != nil {
160
+ c.WriteZeroValueForType(goElemType)
161
+ } else {
162
+ c.WriteZeroValueForType(elemType)
163
+ }
164
+ }
165
+ }
166
+ c.tsw.WriteLiterally("]")
167
+
168
+ // If it's a multi-dimensional array/slice, use depth=2 to convert nested arrays
169
+ if isMultiDimensional {
170
+ c.tsw.WriteLiterally(", 2") // Depth of 2 for one level of nesting
171
+ }
172
+
173
+ c.tsw.WriteLiterally(")")
174
+ return nil
175
+ } else {
176
+ // Check if this is a struct type
177
+ var structType *types.Struct
178
+ isStructLiteral := false
179
+ isAnonymousStruct := false
180
+
181
+ if namedType, ok := litType.(*types.Named); ok {
182
+ if underlyingStruct, ok := namedType.Underlying().(*types.Struct); ok {
183
+ structType = underlyingStruct
184
+ isStructLiteral = true
185
+ // Named struct, use constructor
186
+ c.tsw.WriteLiterally("new ")
187
+ c.WriteTypeExpr(exp.Type)
188
+ }
189
+ } else if ptrType, ok := litType.(*types.Pointer); ok {
190
+ if namedElem, ok := ptrType.Elem().(*types.Named); ok {
191
+ if underlyingStruct, ok := namedElem.Underlying().(*types.Struct); ok {
192
+ structType = underlyingStruct
193
+ isStructLiteral = true // Treat pointer-to-struct literal similarly
194
+ // Named struct pointer, use constructor
195
+ c.tsw.WriteLiterally("new ")
196
+ c.WriteTypeExpr(exp.Type)
197
+ }
198
+ }
199
+ } else if underlyingStruct, ok := litType.Underlying().(*types.Struct); ok {
200
+ // Anonymous struct literal
201
+ structType = underlyingStruct
202
+ isStructLiteral = true
203
+ isAnonymousStruct = true
204
+ // For anonymous structs, don't use constructor, just create object literal
205
+ }
206
+
207
+ if isStructLiteral && structType != nil {
208
+ // --- Struct Literal Handling (Nested) ---
209
+ directFields := make(map[string]ast.Expr)
210
+ embeddedFields := make(map[string]map[string]ast.Expr) // map[EmbeddedPropName]map[FieldName]ValueExpr
211
+ explicitEmbedded := make(map[string]ast.Expr) // Tracks explicitly initialized embedded structs
212
+
213
+ // Pre-populate embeddedFields map keys using the correct property name
214
+ for i := 0; i < structType.NumFields(); i++ {
215
+ field := structType.Field(i)
216
+ if field.Anonymous() {
217
+ fieldType := field.Type()
218
+ if ptr, ok := fieldType.(*types.Pointer); ok {
219
+ fieldType = ptr.Elem()
220
+ }
221
+ if named, ok := fieldType.(*types.Named); ok {
222
+ // Use the type name as the property name in TS
223
+ embeddedPropName := named.Obj().Name()
224
+ embeddedFields[embeddedPropName] = make(map[string]ast.Expr)
225
+ }
226
+ }
227
+ }
228
+
229
+ // Group literal elements by direct vs embedded fields
230
+ for _, elt := range exp.Elts {
231
+ kv, ok := elt.(*ast.KeyValueExpr)
232
+ if !ok {
233
+ continue
234
+ } // Skip non-key-value
235
+ keyIdent, ok := kv.Key.(*ast.Ident)
236
+ if !ok {
237
+ continue
238
+ } // Skip non-ident keys
239
+ keyName := keyIdent.Name
240
+
241
+ // Check if this is an explicit embedded struct initialization
242
+ // e.g., Person: Person{...} or Person: personVar
243
+ if _, isEmbedded := embeddedFields[keyName]; isEmbedded {
244
+ // This is an explicit initialization of an embedded struct
245
+ explicitEmbedded[keyName] = kv.Value
246
+ continue
247
+ }
248
+
249
+ isDirectField := false
250
+ for i := range structType.NumFields() {
251
+ field := structType.Field(i)
252
+ if field.Name() == keyName {
253
+ isDirectField = true
254
+ directFields[keyName] = kv.Value
255
+ break
256
+ }
257
+ }
258
+
259
+ // For anonymous structs, all fields are direct fields
260
+ if isAnonymousStruct {
261
+ directFields[keyName] = kv.Value
262
+ isDirectField = true
263
+ }
264
+
265
+ // If not a direct field, return an error
266
+ if !isDirectField {
267
+ // This field was not found as a direct field in the struct
268
+ return fmt.Errorf("field %s not found in type %s for composite literal",
269
+ keyName, litType.String())
270
+ }
271
+ }
272
+
273
+ // Handle the case where an anonymous struct has values without keys
274
+ // Handle the case where an anonymous struct has values without keys.
275
+ // This block processes non-key-value elements and associates them with struct fields.
276
+ if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 && structType != nil {
277
+ // Check if any elements in the composite literal are not key-value pairs.
278
+ hasNonKeyValueElts := false
279
+ for _, elt := range exp.Elts {
280
+ // If an element is not a key-value pair, set the flag to true.
281
+ if _, isKV := elt.(*ast.KeyValueExpr); !isKV {
282
+ hasNonKeyValueElts = true
283
+ break
284
+ }
285
+ }
286
+
287
+ if hasNonKeyValueElts {
288
+ // Get the fields from the struct type
289
+ for i := 0; i < structType.NumFields(); i++ {
290
+ field := structType.Field(i)
291
+ // If we have a value for this field position
292
+ if i < len(exp.Elts) {
293
+ // Check if it's not a key-value pair
294
+ if _, isKV := exp.Elts[i].(*ast.KeyValueExpr); !isKV {
295
+ directFields[field.Name()] = exp.Elts[i]
296
+ }
297
+ }
298
+ }
299
+ }
300
+ }
301
+
302
+ // Write the object literal
303
+ if isAnonymousStruct {
304
+ // For anonymous structs, just write a simple object literal
305
+ c.tsw.WriteLiterally("{")
306
+ } else {
307
+ // For named structs, write the constructor argument
308
+ c.tsw.WriteLiterally("({")
309
+ }
310
+
311
+ firstFieldWritten := false
312
+
313
+ // Write direct fields that aren't embedded struct names
314
+ directKeys := make([]string, 0, len(directFields))
315
+ for k := range directFields {
316
+ // Skip embedded struct names - we'll handle those separately
317
+ if _, isEmbedded := embeddedFields[k]; !isEmbedded {
318
+ directKeys = append(directKeys, k)
319
+ }
320
+ }
321
+ slices.Sort(directKeys)
322
+ for _, keyName := range directKeys {
323
+ if firstFieldWritten {
324
+ c.tsw.WriteLiterally(", ")
325
+ }
326
+ c.tsw.WriteLiterally(keyName)
327
+ c.tsw.WriteLiterally(": ")
328
+ if err := c.WriteBoxedValue(directFields[keyName]); err != nil {
329
+ return err
330
+ }
331
+ firstFieldWritten = true
332
+ }
333
+
334
+ // Write explicitly initialized embedded structs
335
+ explicitKeys := make([]string, 0, len(explicitEmbedded))
336
+ for k := range explicitEmbedded {
337
+ explicitKeys = append(explicitKeys, k)
338
+ }
339
+ slices.Sort(explicitKeys)
340
+ for _, embeddedName := range explicitKeys {
341
+ if firstFieldWritten {
342
+ c.tsw.WriteLiterally(", ")
343
+ }
344
+ c.tsw.WriteLiterally(embeddedName)
345
+ c.tsw.WriteLiterally(": ")
346
+
347
+ // Check if the embedded value is a composite literal for a struct
348
+ // If so, extract the fields and write them directly
349
+ if compLit, ok := explicitEmbedded[embeddedName].(*ast.CompositeLit); ok {
350
+ // Write initialization fields directly without the 'new Constructor'
351
+ c.tsw.WriteLiterally("{")
352
+ for i, elem := range compLit.Elts {
353
+ if i > 0 {
354
+ c.tsw.WriteLiterally(", ")
355
+ }
356
+ if err := c.WriteBoxedValue(elem); err != nil {
357
+ return err
358
+ }
359
+ }
360
+ c.tsw.WriteLiterally("}")
361
+ } else {
362
+ // Not a composite literal, write it normally
363
+ if err := c.WriteBoxedValue(explicitEmbedded[embeddedName]); err != nil {
364
+ return err
365
+ }
366
+ }
367
+ firstFieldWritten = true
368
+ }
369
+
370
+ // Write embedded fields for structs that weren't explicitly initialized
371
+ embeddedKeys := make([]string, 0, len(embeddedFields))
372
+ for k := range embeddedFields {
373
+ // Skip embedded structs that were explicitly initialized
374
+ if _, wasExplicit := explicitEmbedded[k]; !wasExplicit {
375
+ embeddedKeys = append(embeddedKeys, k)
376
+ }
377
+ }
378
+ slices.Sort(embeddedKeys)
379
+ for _, embeddedPropName := range embeddedKeys {
380
+ fieldsMap := embeddedFields[embeddedPropName]
381
+ if len(fieldsMap) == 0 {
382
+ continue
383
+ } // Skip empty embedded initializers
384
+
385
+ if firstFieldWritten {
386
+ c.tsw.WriteLiterally(", ")
387
+ }
388
+ c.tsw.WriteLiterally(embeddedPropName) // Use the Type name as the property key
389
+ c.tsw.WriteLiterally(": {")
390
+
391
+ innerKeys := make([]string, 0, len(fieldsMap))
392
+ for k := range fieldsMap {
393
+ innerKeys = append(innerKeys, k)
394
+ }
395
+ slices.Sort(innerKeys)
396
+ for i, keyName := range innerKeys {
397
+ if i > 0 {
398
+ c.tsw.WriteLiterally(", ")
399
+ }
400
+ c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
401
+ c.tsw.WriteLiterally(": ")
402
+ if err := c.WriteBoxedValue(fieldsMap[keyName]); err != nil {
403
+ return err
404
+ }
405
+ }
406
+ c.tsw.WriteLiterally("}")
407
+ firstFieldWritten = true
408
+ }
409
+
410
+ // Close the object literal
411
+ if isAnonymousStruct {
412
+ c.tsw.WriteLiterally("}")
413
+ } else {
414
+ c.tsw.WriteLiterally("})")
415
+ }
416
+
417
+ } else {
418
+ // Non-struct type or anonymous struct, handle normally (or potentially error for anonymous struct literals?)
419
+ c.tsw.WriteLiterally("({") // Assuming object literal for constructor
420
+ for i, elm := range exp.Elts {
421
+ if i != 0 {
422
+ c.tsw.WriteLiterally(", ")
423
+ }
424
+ if err := c.WriteBoxedValue(elm); err != nil {
425
+ return fmt.Errorf("failed to write literal field: %w", err)
426
+ }
427
+ }
428
+ c.tsw.WriteLiterally("})")
429
+ }
430
+ return nil
431
+ }
432
+ }
433
+
434
+ // Untyped composite literal. Let's use type information to determine what it is.
435
+ // First try to get the type information for the expression
436
+ isObject := false
437
+ if tv, ok := c.pkg.TypesInfo.Types[exp]; ok && tv.Type != nil {
438
+ underlying := tv.Type.Underlying()
439
+ switch underlying.(type) {
440
+ case *types.Map, *types.Struct:
441
+ isObject = true
442
+ case *types.Array, *types.Slice:
443
+ isObject = false
444
+ default:
445
+ return fmt.Errorf("unhandled composite literal type: %T", underlying)
446
+ }
447
+ } else {
448
+ return fmt.Errorf("could not determine composite literal type from type information")
449
+ }
450
+
451
+ if isObject {
452
+ c.tsw.WriteLiterally("{ ")
453
+ } else {
454
+ c.tsw.WriteLiterally("[ ")
455
+ }
456
+
457
+ for i, elm := range exp.Elts {
458
+ if i != 0 {
459
+ c.tsw.WriteLiterally(", ")
460
+ }
461
+ if err := c.WriteBoxedValue(elm); err != nil {
462
+ return fmt.Errorf("failed to write untyped composite literal element: %w", err)
463
+ }
464
+ }
465
+ if isObject {
466
+ c.tsw.WriteLiterally(" }")
467
+ } else {
468
+ c.tsw.WriteLiterally(" ]")
469
+ }
470
+ return nil
471
+ }
472
+
473
+ // WriteBoxedValue translates a Go expression (`ast.Expr`) into its TypeScript equivalent,
474
+ // specifically for use as a value within a composite literal (e.g., struct fields,
475
+ // map keys/values, or array/slice elements). Its primary goal is to ensure that the
476
+ // actual, unboxed value of the expression is used.
477
+ //
478
+ // How it works:
479
+ // - Identifiers (`*ast.Ident`): Delegates to `c.WriteIdent(ident, true)`, forcing
480
+ // the `accessValue` flag to `true`. This ensures that if `ident` refers to a
481
+ // GoScript boxed variable, the generated TypeScript accesses its underlying `.value`
482
+ // (e.g., `myVar.value`).
483
+ // - Selector Expressions (`*ast.SelectorExpr`, e.g., `obj.Field`): Delegates to
484
+ // `c.WriteSelectorExpr(e)`. This function handles the necessary logic for
485
+ // accessing fields or methods, including any required unboxing if the field
486
+ // itself or the object it's accessed on is boxed (e.g., `obj.value.field` or
487
+ // `obj.field.value`).
488
+ // - Star Expressions (`*ast.StarExpr`, e.g., `*ptr`): Delegates to `c.WriteStarExpr(e)`.
489
+ // This function handles pointer dereferencing, which in GoScript's boxing model
490
+ // often translates to accessing the `.value` field of the pointer (e.g., `ptr.value`).
491
+ // - Basic Literals (`*ast.BasicLit`, e.g., `123`, `"hello"`): Delegates to
492
+ // `c.WriteBasicLit(e)` for direct translation.
493
+ // - Other expression types: Falls back to `c.WriteValueExpr(expr)` for general
494
+ // expression handling. This is important for complex expressions like function
495
+ // calls or binary operations that might appear as values within a composite literal.
496
+ //
497
+ // Necessity and Distinction from `WriteValueExpr`:
498
+ // While `WriteValueExpr` is a general-purpose function for translating Go expressions
499
+ // and also unboxes identifiers (by calling `WriteIdent` with `accessValue: true`),
500
+ // `WriteBoxedValue` serves a specific and crucial role when called from `WriteCompositeLit`:
501
+ // 1. Clarity of Intent: It explicitly signals that for the constituents of a composite
502
+ // literal, the *unboxed value* is mandatory.
503
+ // 2. Contract for `WriteCompositeLit`: It ensures that `WriteCompositeLit` receives
504
+ // the correct values for initialization, insulating it from potential changes in
505
+ // the default behavior of `WriteValueExpr` regarding unboxing.
506
+ // 3. Prevents Recursion: `WriteValueExpr` handles `*ast.CompositeLit` nodes by
507
+ // calling `WriteCompositeLit`. If `WriteCompositeLit` were to directly call
508
+ // `WriteValueExpr` for its elements, it could lead to unintended recursion or
509
+ // behavior if an element itself was another composite literal. `WriteBoxedValue`
510
+ // acts as a specific intermediary for the *elements*.
511
+ //
512
+ // In summary, `WriteBoxedValue` is a specialized dispatcher used by `WriteCompositeLit`
513
+ // to guarantee that all parts of a Go composite literal are initialized with their
514
+ // proper, unboxed TypeScript values.
515
+ func (c *GoToTSCompiler) WriteBoxedValue(expr ast.Expr) error {
516
+ if expr == nil {
517
+ return fmt.Errorf("nil expression passed to writeBoxedValue")
518
+ }
519
+
520
+ // Handle different expression types
521
+ switch e := expr.(type) {
522
+ case *ast.Ident:
523
+ c.WriteIdent(e, true)
524
+ return nil
525
+ case *ast.SelectorExpr:
526
+ return c.WriteSelectorExpr(e)
527
+ case *ast.StarExpr:
528
+ // For star expressions, delegate to WriteStarExpr which handles dereferencing
529
+ return c.WriteStarExpr(e)
530
+ case *ast.BasicLit:
531
+ c.WriteBasicLit(e)
532
+ return nil
533
+ default:
534
+ // For other expression types, use WriteValueExpr
535
+ return c.WriteValueExpr(expr)
536
+ }
537
+ }
@@ -17,6 +17,9 @@ type Config struct {
17
17
  OutputPathRoot string
18
18
  // BuildFlags are the Go build flags (tags) to use during analysis.
19
19
  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.
22
+ AllDependencies bool
20
23
  }
21
24
 
22
25
  // Validate checks the config.