@player-lang/functional-dsl-generator 0.0.2-next.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 (42) hide show
  1. package/dist/cjs/index.cjs +2146 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/index.legacy-esm.js +2075 -0
  4. package/dist/index.mjs +2075 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +38 -0
  7. package/src/__tests__/__snapshots__/generator.test.ts.snap +886 -0
  8. package/src/__tests__/builder-class-generator.test.ts +627 -0
  9. package/src/__tests__/cli.test.ts +685 -0
  10. package/src/__tests__/default-value-generator.test.ts +365 -0
  11. package/src/__tests__/generator.test.ts +2860 -0
  12. package/src/__tests__/import-generator.test.ts +444 -0
  13. package/src/__tests__/path-utils.test.ts +174 -0
  14. package/src/__tests__/type-collector.test.ts +674 -0
  15. package/src/__tests__/type-transformer.test.ts +934 -0
  16. package/src/__tests__/utils.test.ts +597 -0
  17. package/src/builder-class-generator.ts +254 -0
  18. package/src/cli.ts +285 -0
  19. package/src/default-value-generator.ts +307 -0
  20. package/src/generator.ts +257 -0
  21. package/src/import-generator.ts +331 -0
  22. package/src/index.ts +38 -0
  23. package/src/path-utils.ts +155 -0
  24. package/src/ts-morph-type-finder.ts +319 -0
  25. package/src/type-categorizer.ts +131 -0
  26. package/src/type-collector.ts +296 -0
  27. package/src/type-resolver.ts +266 -0
  28. package/src/type-transformer.ts +487 -0
  29. package/src/utils.ts +762 -0
  30. package/types/builder-class-generator.d.ts +56 -0
  31. package/types/cli.d.ts +6 -0
  32. package/types/default-value-generator.d.ts +74 -0
  33. package/types/generator.d.ts +102 -0
  34. package/types/import-generator.d.ts +77 -0
  35. package/types/index.d.ts +12 -0
  36. package/types/path-utils.d.ts +65 -0
  37. package/types/ts-morph-type-finder.d.ts +73 -0
  38. package/types/type-categorizer.d.ts +46 -0
  39. package/types/type-collector.d.ts +62 -0
  40. package/types/type-resolver.d.ts +49 -0
  41. package/types/type-transformer.d.ts +74 -0
  42. package/types/utils.d.ts +205 -0
@@ -0,0 +1,674 @@
1
+ import { describe, test, expect, beforeEach } from "vitest";
2
+ import { setupTestEnv } from "@player-lang/test-utils";
3
+ import { TsConverter } from "@xlr-lib/xlr-converters";
4
+ import type { NamedType, ObjectType } from "@xlr-lib/xlr";
5
+ import { TypeCollector, type TypeTracker } from "../type-collector";
6
+ import type { TypeRegistry } from "../utils";
7
+
8
+ /** Custom primitives that should be treated as refs rather than resolved */
9
+ const CUSTOM_PRIMITIVES = ["Asset", "AssetWrapper", "Binding", "Expression"];
10
+
11
+ /**
12
+ * Converts TypeScript source code to XLR types
13
+ */
14
+ function convertTsToXLR(
15
+ sourceCode: string,
16
+ customPrimitives = CUSTOM_PRIMITIVES,
17
+ ) {
18
+ const { sf, tc } = setupTestEnv(sourceCode);
19
+ const converter = new TsConverter(tc, customPrimitives);
20
+ return converter.convertSourceFile(sf).data.types;
21
+ }
22
+
23
+ /**
24
+ * Mock TypeTracker for testing
25
+ */
26
+ class MockTypeTracker implements TypeTracker {
27
+ readonly trackedTypes: string[] = [];
28
+ readonly trackedNamespaces: string[] = [];
29
+
30
+ trackReferencedType(typeName: string): void {
31
+ this.trackedTypes.push(typeName);
32
+ }
33
+
34
+ trackNamespaceImport(namespaceName: string): void {
35
+ this.trackedNamespaces.push(namespaceName);
36
+ }
37
+ }
38
+
39
+ describe("TypeCollector", () => {
40
+ let tracker: MockTypeTracker;
41
+ let genericParamSymbols: Set<string>;
42
+ let namespaceMemberMap: Map<string, string>;
43
+
44
+ beforeEach(() => {
45
+ tracker = new MockTypeTracker();
46
+ genericParamSymbols = new Set<string>();
47
+ namespaceMemberMap = new Map<string, string>();
48
+ });
49
+
50
+ function createCollector(
51
+ mainTypeName: string,
52
+ typeRegistry?: TypeRegistry,
53
+ ): TypeCollector {
54
+ return new TypeCollector(
55
+ tracker,
56
+ genericParamSymbols,
57
+ mainTypeName,
58
+ namespaceMemberMap,
59
+ typeRegistry,
60
+ );
61
+ }
62
+
63
+ describe("collectGenericParamSymbols", () => {
64
+ test("collects generic parameters from type definition", () => {
65
+ const source = `
66
+ interface Asset<T extends string = string> {
67
+ id: string;
68
+ type: T;
69
+ }
70
+
71
+ export interface GenericAsset<T, U extends string = "default"> extends Asset<"generic"> {
72
+ prop1?: T;
73
+ prop2?: U;
74
+ }
75
+ `;
76
+
77
+ const types = convertTsToXLR(source);
78
+ const asset = types.find(
79
+ (t) => t.name === "GenericAsset",
80
+ ) as NamedType<ObjectType>;
81
+
82
+ const collector = createCollector("GenericAsset");
83
+ collector.collectGenericParamSymbols(asset);
84
+
85
+ expect(genericParamSymbols.has("T")).toBe(true);
86
+ expect(genericParamSymbols.has("U")).toBe(true);
87
+ });
88
+
89
+ test("does nothing for non-generic types", () => {
90
+ const source = `
91
+ interface Asset<T extends string = string> {
92
+ id: string;
93
+ type: T;
94
+ }
95
+
96
+ export interface TextAsset extends Asset<"text"> {
97
+ value: string;
98
+ }
99
+ `;
100
+
101
+ const types = convertTsToXLR(source);
102
+ const asset = types.find(
103
+ (t) => t.name === "TextAsset",
104
+ ) as NamedType<ObjectType>;
105
+
106
+ const collector = createCollector("TextAsset");
107
+ collector.collectGenericParamSymbols(asset);
108
+
109
+ expect(genericParamSymbols.size).toBe(0);
110
+ });
111
+
112
+ test("collects inherited generic parameters from base type via typeRegistry", () => {
113
+ const source = `
114
+ interface Asset<T extends string = string> {
115
+ id: string;
116
+ type: T;
117
+ }
118
+ type AssetWrapper<T extends Asset = Asset> = { asset: T };
119
+
120
+ export interface FileInputAssetBase<AnyAsset extends Asset = Asset> extends Asset<'fileInput'> {
121
+ label?: AssetWrapper<AnyAsset>;
122
+ }
123
+
124
+ export interface FileInputAsset extends FileInputAssetBase {
125
+ binding: { progressAmount: string };
126
+ }
127
+ `;
128
+
129
+ const types = convertTsToXLR(source);
130
+ const fileInputBase = types.find(
131
+ (t) => t.name === "FileInputAssetBase",
132
+ ) as NamedType<ObjectType>;
133
+ const fileInputAsset = types.find(
134
+ (t) => t.name === "FileInputAsset",
135
+ ) as NamedType<ObjectType>;
136
+
137
+ const typeRegistry: TypeRegistry = new Map([
138
+ ["FileInputAssetBase", fileInputBase],
139
+ ]);
140
+ const collector = createCollector("FileInputAsset", typeRegistry);
141
+ collector.collectGenericParamSymbols(fileInputAsset);
142
+ collector.collectReferencedTypes(fileInputAsset);
143
+
144
+ // AnyAsset should be recognized as an inherited generic param, not a concrete type
145
+ expect(genericParamSymbols.has("AnyAsset")).toBe(true);
146
+ expect(tracker.trackedTypes).not.toContain("AnyAsset");
147
+ });
148
+
149
+ test("does not mark generic param as excluded when it collides with a concrete type in registry", () => {
150
+ const source = `
151
+ interface Asset<T extends string = string> {
152
+ id: string;
153
+ type: T;
154
+ }
155
+
156
+ export interface Metadata {
157
+ beacon?: string;
158
+ }
159
+
160
+ export interface GenericBase<Metadata extends Asset = Asset> extends Asset<'base'> {
161
+ slot?: Metadata;
162
+ }
163
+
164
+ export interface ConcreteChild extends GenericBase {
165
+ extra: string;
166
+ }
167
+ `;
168
+
169
+ const types = convertTsToXLR(source);
170
+ const metadataType = types.find(
171
+ (t) => t.name === "Metadata",
172
+ ) as NamedType<ObjectType>;
173
+ const genericBase = types.find(
174
+ (t) => t.name === "GenericBase",
175
+ ) as NamedType<ObjectType>;
176
+ const concreteChild = types.find(
177
+ (t) => t.name === "ConcreteChild",
178
+ ) as NamedType<ObjectType>;
179
+
180
+ // Registry has both GenericBase (with generic param "Metadata") and the concrete "Metadata" type
181
+ const typeRegistry: TypeRegistry = new Map([
182
+ ["GenericBase", genericBase],
183
+ ["Metadata", metadataType],
184
+ ]);
185
+ const collector = createCollector("ConcreteChild", typeRegistry);
186
+ collector.collectGenericParamSymbols(concreteChild);
187
+
188
+ // "Metadata" should NOT be in genericParamSymbols because it's also a concrete type
189
+ expect(genericParamSymbols.has("Metadata")).toBe(false);
190
+ });
191
+ });
192
+
193
+ describe("collectReferencedTypes", () => {
194
+ test("collects referenced named types", () => {
195
+ const source = `
196
+ interface Asset<T extends string = string> {
197
+ id: string;
198
+ type: T;
199
+ }
200
+
201
+ export interface Metadata {
202
+ beacon?: string;
203
+ }
204
+
205
+ export interface ActionAsset extends Asset<"action"> {
206
+ value?: string;
207
+ metaData?: Metadata;
208
+ }
209
+ `;
210
+
211
+ const types = convertTsToXLR(source);
212
+ const asset = types.find(
213
+ (t) => t.name === "ActionAsset",
214
+ ) as NamedType<ObjectType>;
215
+
216
+ const collector = createCollector("ActionAsset");
217
+ collector.collectReferencedTypes(asset);
218
+
219
+ expect(tracker.trackedTypes).toContain("Metadata");
220
+ });
221
+
222
+ test("does not collect the main type itself", () => {
223
+ const source = `
224
+ interface Asset<T extends string = string> {
225
+ id: string;
226
+ type: T;
227
+ }
228
+
229
+ export interface TextAsset extends Asset<"text"> {
230
+ value: string;
231
+ }
232
+ `;
233
+
234
+ const types = convertTsToXLR(source);
235
+ const asset = types.find(
236
+ (t) => t.name === "TextAsset",
237
+ ) as NamedType<ObjectType>;
238
+
239
+ const collector = createCollector("TextAsset");
240
+ collector.collectReferencedTypes(asset);
241
+
242
+ expect(tracker.trackedTypes).not.toContain("TextAsset");
243
+ });
244
+
245
+ test("does not collect built-in types", () => {
246
+ const source = `
247
+ interface Asset<T extends string = string> {
248
+ id: string;
249
+ type: T;
250
+ }
251
+
252
+ export interface DataAsset extends Asset<"data"> {
253
+ items?: Array<string>;
254
+ mapping?: Record<string, number>;
255
+ cache?: Map<string, object>;
256
+ }
257
+ `;
258
+
259
+ const types = convertTsToXLR(source);
260
+ const asset = types.find(
261
+ (t) => t.name === "DataAsset",
262
+ ) as NamedType<ObjectType>;
263
+
264
+ const collector = createCollector("DataAsset");
265
+ collector.collectReferencedTypes(asset);
266
+
267
+ expect(tracker.trackedTypes).not.toContain("Array");
268
+ expect(tracker.trackedTypes).not.toContain("Record");
269
+ expect(tracker.trackedTypes).not.toContain("Map");
270
+ });
271
+
272
+ test("collects types from array element types", () => {
273
+ const source = `
274
+ interface Asset<T extends string = string> {
275
+ id: string;
276
+ type: T;
277
+ }
278
+
279
+ export interface Item {
280
+ name: string;
281
+ }
282
+
283
+ export interface CollectionAsset extends Asset<"collection"> {
284
+ items?: Array<Item>;
285
+ }
286
+ `;
287
+
288
+ const types = convertTsToXLR(source);
289
+ const asset = types.find(
290
+ (t) => t.name === "CollectionAsset",
291
+ ) as NamedType<ObjectType>;
292
+
293
+ const collector = createCollector("CollectionAsset");
294
+ collector.collectReferencedTypes(asset);
295
+
296
+ expect(tracker.trackedTypes).toContain("Item");
297
+ });
298
+
299
+ test("collects types from union types", () => {
300
+ const source = `
301
+ interface Asset<T extends string = string> {
302
+ id: string;
303
+ type: T;
304
+ }
305
+
306
+ export interface TypeA {
307
+ a: string;
308
+ }
309
+
310
+ export interface TypeB {
311
+ b: number;
312
+ }
313
+
314
+ export interface UnionAsset extends Asset<"union"> {
315
+ value?: TypeA | TypeB;
316
+ }
317
+ `;
318
+
319
+ const types = convertTsToXLR(source);
320
+ const asset = types.find(
321
+ (t) => t.name === "UnionAsset",
322
+ ) as NamedType<ObjectType>;
323
+
324
+ const collector = createCollector("UnionAsset");
325
+ collector.collectReferencedTypes(asset);
326
+
327
+ expect(tracker.trackedTypes).toContain("TypeA");
328
+ expect(tracker.trackedTypes).toContain("TypeB");
329
+ });
330
+
331
+ test("collects types from intersection types", () => {
332
+ const source = `
333
+ interface Asset<T extends string = string> {
334
+ id: string;
335
+ type: T;
336
+ }
337
+
338
+ export interface BaseProps {
339
+ name: string;
340
+ }
341
+
342
+ export interface ExtendedProps {
343
+ description: string;
344
+ }
345
+
346
+ export interface IntersectionAsset extends Asset<"intersection"> {
347
+ combined?: BaseProps & ExtendedProps;
348
+ }
349
+ `;
350
+
351
+ const types = convertTsToXLR(source);
352
+ const asset = types.find(
353
+ (t) => t.name === "IntersectionAsset",
354
+ ) as NamedType<ObjectType>;
355
+
356
+ const collector = createCollector("IntersectionAsset");
357
+ collector.collectReferencedTypes(asset);
358
+
359
+ expect(tracker.trackedTypes).toContain("BaseProps");
360
+ expect(tracker.trackedTypes).toContain("ExtendedProps");
361
+ });
362
+
363
+ test("does not collect generic parameters as types", () => {
364
+ const source = `
365
+ interface Asset<T extends string = string> {
366
+ id: string;
367
+ type: T;
368
+ }
369
+ type AssetWrapper<T extends Asset = Asset> = { asset: T };
370
+
371
+ export interface GenericAsset<AnyAsset extends Asset = Asset> extends Asset<"generic"> {
372
+ slot?: AssetWrapper<AnyAsset>;
373
+ }
374
+ `;
375
+
376
+ const types = convertTsToXLR(source);
377
+ const asset = types.find(
378
+ (t) => t.name === "GenericAsset",
379
+ ) as NamedType<ObjectType>;
380
+
381
+ // First collect generic params
382
+ const collector = createCollector("GenericAsset");
383
+ collector.collectGenericParamSymbols(asset);
384
+
385
+ // Then collect referenced types
386
+ collector.collectReferencedTypes(asset);
387
+
388
+ // AnyAsset should NOT be tracked since it's a generic param
389
+ expect(tracker.trackedTypes).not.toContain("AnyAsset");
390
+ });
391
+ });
392
+
393
+ describe("collectTypesFromGenericTokens", () => {
394
+ test("collects types from generic constraints", () => {
395
+ const source = `
396
+ interface Asset<T extends string = string> {
397
+ id: string;
398
+ type: T;
399
+ }
400
+
401
+ export interface Bar {
402
+ value: string;
403
+ }
404
+
405
+ export interface GenericAsset<T extends Bar> extends Asset<"generic"> {
406
+ data?: T;
407
+ }
408
+ `;
409
+
410
+ const types = convertTsToXLR(source);
411
+ const asset = types.find(
412
+ (t) => t.name === "GenericAsset",
413
+ ) as NamedType<ObjectType>;
414
+
415
+ const collector = createCollector("GenericAsset");
416
+ collector.collectGenericParamSymbols(asset);
417
+ collector.collectTypesFromGenericTokens(asset);
418
+
419
+ expect(tracker.trackedTypes).toContain("Bar");
420
+ });
421
+
422
+ test("collects types from generic defaults", () => {
423
+ const source = `
424
+ interface Asset<T extends string = string> {
425
+ id: string;
426
+ type: T;
427
+ }
428
+
429
+ export interface DefaultType {
430
+ value: string;
431
+ }
432
+
433
+ export interface GenericAsset<T = DefaultType> extends Asset<"generic"> {
434
+ data?: T;
435
+ }
436
+ `;
437
+
438
+ const types = convertTsToXLR(source);
439
+ const asset = types.find(
440
+ (t) => t.name === "GenericAsset",
441
+ ) as NamedType<ObjectType>;
442
+
443
+ const collector = createCollector("GenericAsset");
444
+ collector.collectGenericParamSymbols(asset);
445
+ collector.collectTypesFromGenericTokens(asset);
446
+
447
+ expect(tracker.trackedTypes).toContain("DefaultType");
448
+ });
449
+
450
+ test("collects nested types from generic constraints", () => {
451
+ const source = `
452
+ interface Asset<T extends string = string> {
453
+ id: string;
454
+ type: T;
455
+ }
456
+ type AssetWrapper<T extends Asset = Asset> = { asset: T };
457
+
458
+ export interface ListItemNoHelp<AnyAsset extends Asset = Asset>
459
+ extends AssetWrapper<AnyAsset> {}
460
+
461
+ export interface ListItem<AnyAsset extends Asset = Asset>
462
+ extends ListItemNoHelp<AnyAsset> {
463
+ help?: { id: string; };
464
+ }
465
+
466
+ export interface ListAsset<
467
+ AnyAsset extends Asset = Asset,
468
+ ItemType extends ListItemNoHelp<AnyAsset> = ListItem<AnyAsset>
469
+ > extends Asset<"list"> {
470
+ values?: Array<ItemType>;
471
+ }
472
+ `;
473
+
474
+ const types = convertTsToXLR(source);
475
+ const asset = types.find(
476
+ (t) => t.name === "ListAsset",
477
+ ) as NamedType<ObjectType>;
478
+
479
+ const collector = createCollector("ListAsset");
480
+ collector.collectGenericParamSymbols(asset);
481
+ collector.collectTypesFromGenericTokens(asset);
482
+
483
+ expect(tracker.trackedTypes).toContain("ListItemNoHelp");
484
+ expect(tracker.trackedTypes).toContain("ListItem");
485
+ // AnyAsset should NOT be tracked since it's a generic param
486
+ expect(tracker.trackedTypes).not.toContain("AnyAsset");
487
+ });
488
+ });
489
+
490
+ describe("Namespaced Types", () => {
491
+ test("tracks namespace for namespaced types via collectReferencedTypesFromNode", () => {
492
+ // Test the internal method directly with a namespaced ref
493
+ // because TypeScript namespaces may not be fully preserved through XLR conversion
494
+ const collector = createCollector("InputAsset");
495
+
496
+ // Manually invoke the collector with a node that has a namespaced ref
497
+ const namedType: NamedType<ObjectType> = {
498
+ type: "object",
499
+ name: "InputAsset",
500
+ properties: {
501
+ validation: {
502
+ required: false,
503
+ node: { type: "ref", ref: "Validation.CrossfieldReference" },
504
+ },
505
+ },
506
+ };
507
+
508
+ collector.collectReferencedTypes(namedType);
509
+
510
+ expect(tracker.trackedNamespaces).toContain("Validation");
511
+ expect(namespaceMemberMap.get("CrossfieldReference")).toBe(
512
+ "Validation.CrossfieldReference",
513
+ );
514
+ });
515
+
516
+ test("collects types from nested namespaces (A.B.C)", () => {
517
+ const collector = createCollector("ComplexAsset");
518
+
519
+ const namedType: NamedType<ObjectType> = {
520
+ type: "object",
521
+ name: "ComplexAsset",
522
+ properties: {
523
+ deepRef: {
524
+ required: false,
525
+ node: { type: "ref", ref: "A.B.C.DeepType" },
526
+ },
527
+ },
528
+ };
529
+
530
+ collector.collectReferencedTypes(namedType);
531
+
532
+ expect(tracker.trackedNamespaces).toContain("A");
533
+ expect(namespaceMemberMap.get("B.C.DeepType")).toBe("A.B.C.DeepType");
534
+ });
535
+
536
+ test("handles namespace members with generics", () => {
537
+ const collector = createCollector("AdvancedAsset");
538
+
539
+ const namedType: NamedType<ObjectType> = {
540
+ type: "object",
541
+ name: "AdvancedAsset",
542
+ properties: {
543
+ config: {
544
+ required: false,
545
+ node: {
546
+ type: "ref",
547
+ ref: "Config.Options",
548
+ genericArguments: [{ type: "string" }],
549
+ },
550
+ },
551
+ },
552
+ };
553
+
554
+ collector.collectReferencedTypes(namedType);
555
+
556
+ expect(tracker.trackedNamespaces).toContain("Config");
557
+ expect(namespaceMemberMap.get("Options")).toBe("Config.Options");
558
+ });
559
+
560
+ test("tracks namespace imports separately from type imports", () => {
561
+ const collector = createCollector("MixedAsset");
562
+
563
+ const namedType: NamedType<ObjectType> = {
564
+ type: "object",
565
+ name: "MixedAsset",
566
+ properties: {
567
+ regular: {
568
+ required: false,
569
+ node: { type: "ref", ref: "RegularType" },
570
+ },
571
+ namespaced: {
572
+ required: false,
573
+ node: { type: "ref", ref: "NS.NamespacedType" },
574
+ },
575
+ },
576
+ };
577
+
578
+ collector.collectReferencedTypes(namedType);
579
+
580
+ expect(tracker.trackedTypes).toContain("RegularType");
581
+ expect(tracker.trackedTypes).not.toContain("NS.NamespacedType");
582
+ expect(tracker.trackedNamespaces).toContain("NS");
583
+ });
584
+ });
585
+
586
+ describe("Generic Arguments (Edge Cases)", () => {
587
+ test("collects types from nested generic arguments", () => {
588
+ const collector = createCollector("NestedGenericAsset");
589
+
590
+ const namedType: NamedType<ObjectType> = {
591
+ type: "object",
592
+ name: "NestedGenericAsset",
593
+ properties: {
594
+ nested: {
595
+ required: false,
596
+ node: {
597
+ type: "ref",
598
+ ref: "Outer",
599
+ genericArguments: [
600
+ {
601
+ type: "ref",
602
+ ref: "Middle",
603
+ genericArguments: [{ type: "ref", ref: "Inner" }],
604
+ },
605
+ ],
606
+ },
607
+ },
608
+ },
609
+ };
610
+
611
+ collector.collectReferencedTypes(namedType);
612
+
613
+ expect(tracker.trackedTypes).toContain("Outer");
614
+ expect(tracker.trackedTypes).toContain("Middle");
615
+ expect(tracker.trackedTypes).toContain("Inner");
616
+ });
617
+
618
+ test("does not collect primitive type arguments", () => {
619
+ const collector = createCollector("PrimitiveGenericAsset");
620
+
621
+ const namedType: NamedType<ObjectType> = {
622
+ type: "object",
623
+ name: "PrimitiveGenericAsset",
624
+ properties: {
625
+ config: {
626
+ required: false,
627
+ node: {
628
+ type: "ref",
629
+ ref: "Container",
630
+ genericArguments: [{ type: "string" }, { type: "number" }],
631
+ },
632
+ },
633
+ },
634
+ };
635
+
636
+ collector.collectReferencedTypes(namedType);
637
+
638
+ expect(tracker.trackedTypes).toContain("Container");
639
+ expect(tracker.trackedTypes).not.toContain("string");
640
+ expect(tracker.trackedTypes).not.toContain("number");
641
+ });
642
+
643
+ test("does not collect generic parameter symbols as types to import", () => {
644
+ genericParamSymbols.add("T");
645
+ genericParamSymbols.add("U");
646
+
647
+ const collector = createCollector("GenericContainerAsset");
648
+
649
+ const namedType: NamedType<ObjectType> = {
650
+ type: "object",
651
+ name: "GenericContainerAsset",
652
+ properties: {
653
+ container: {
654
+ required: false,
655
+ node: {
656
+ type: "ref",
657
+ ref: "Container",
658
+ genericArguments: [
659
+ { type: "ref", ref: "T" },
660
+ { type: "ref", ref: "U" },
661
+ ],
662
+ },
663
+ },
664
+ },
665
+ };
666
+
667
+ collector.collectReferencedTypes(namedType);
668
+
669
+ expect(tracker.trackedTypes).toContain("Container");
670
+ expect(tracker.trackedTypes).not.toContain("T");
671
+ expect(tracker.trackedTypes).not.toContain("U");
672
+ });
673
+ });
674
+ });