typia 3.8.0-dev.20230416 → 3.8.0-dev.20230417

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 (50) hide show
  1. package/README.md +35 -228
  2. package/lib/factories/TypiaFileFactory.js +9 -4
  3. package/lib/factories/TypiaFileFactory.js.map +1 -1
  4. package/package.json +1 -1
  5. package/src/IRandomGenerator.ts +33 -33
  6. package/src/executable/TypiaGenerateWizard.ts +85 -85
  7. package/src/executable/TypiaSetupWizard.ts +118 -118
  8. package/src/executable/setup/ArgumentParser.ts +45 -45
  9. package/src/executable/setup/CommandExecutor.ts +8 -8
  10. package/src/executable/setup/FileRetriever.ts +22 -22
  11. package/src/executable/setup/PackageManager.ts +71 -71
  12. package/src/executable/setup/PluginConfigurator.ts +59 -59
  13. package/src/executable/typia.ts +52 -52
  14. package/src/factories/IdentifierFactory.ts +59 -59
  15. package/src/factories/MetadataTagFactory.ts +302 -302
  16. package/src/factories/TypiaFileFactory.ts +12 -3
  17. package/src/metadata/ICommentTag.ts +4 -4
  18. package/src/metadata/Metadata.ts +533 -533
  19. package/src/module.ts +2043 -2043
  20. package/src/programmers/AssertProgrammer.ts +284 -284
  21. package/src/programmers/CheckerProgrammer.ts +920 -920
  22. package/src/programmers/LiteralsProgrammer.ts +65 -65
  23. package/src/programmers/RandomProgrammer.ts +413 -413
  24. package/src/programmers/ValidateProgrammer.ts +317 -317
  25. package/src/programmers/helpers/RandomJoiner.ts +161 -161
  26. package/src/programmers/helpers/RandomRanger.ts +216 -216
  27. package/src/programmers/internal/application_native.ts +32 -32
  28. package/src/programmers/internal/check_array.ts +30 -30
  29. package/src/programmers/internal/check_array_length.ts +35 -35
  30. package/src/programmers/internal/check_custom.ts +33 -33
  31. package/src/programmers/internal/check_number.ts +177 -177
  32. package/src/programmers/internal/check_object.ts +55 -55
  33. package/src/programmers/internal/check_string_tags.ts +67 -67
  34. package/src/programmers/internal/check_template.ts +56 -56
  35. package/src/programmers/internal/check_union_array_like.ts +272 -272
  36. package/src/programmers/internal/feature_object_entries.ts +63 -63
  37. package/src/programmers/internal/get_comment_tags.ts +23 -23
  38. package/src/programmers/internal/metadata_to_pattern.ts +34 -34
  39. package/src/programmers/internal/random_custom.ts +30 -30
  40. package/src/programmers/internal/stringify_dynamic_properties.ts +168 -168
  41. package/src/programmers/internal/stringify_regular_properties.ts +84 -84
  42. package/src/transformers/CallExpressionTransformer.ts +174 -174
  43. package/src/transformers/ImportTransformer.ts +66 -66
  44. package/src/transformers/features/miscellaneous/ApplicationTransformer.ts +119 -119
  45. package/src/transformers/features/miscellaneous/CreateRandomTransformer.ts +41 -41
  46. package/src/transformers/features/miscellaneous/LiteralsTransformer.ts +30 -30
  47. package/src/transformers/features/miscellaneous/MetadataTransformer.ts +54 -54
  48. package/src/transformers/features/miscellaneous/RandomTransformer.ts +46 -46
  49. package/src/typings/Customizable.ts +5 -5
  50. package/src/utils/RandomGenerator.ts +93 -93
@@ -1,920 +1,920 @@
1
- import ts from "typescript";
2
-
3
- import { ExpressionFactory } from "../factories/ExpressionFactory";
4
- import { IdentifierFactory } from "../factories/IdentifierFactory";
5
- import { MetadataCollection } from "../factories/MetadataCollection";
6
- import { MetadataFactory } from "../factories/MetadataFactory";
7
- import { TypeFactory } from "../factories/TypeFactory";
8
- import { ValueFactory } from "../factories/ValueFactory";
9
-
10
- import { IMetadataTag } from "../metadata/IMetadataTag";
11
- import { Metadata } from "../metadata/Metadata";
12
- import { MetadataObject } from "../metadata/MetadataObject";
13
-
14
- import { IProject } from "../transformers/IProject";
15
-
16
- import { FeatureProgrammer } from "./FeatureProgrammer";
17
- import { AtomicPredicator } from "./helpers/AtomicPredicator";
18
- import { FunctionImporter } from "./helpers/FunctionImporeter";
19
- import { ICheckEntry } from "./helpers/ICheckEntry";
20
- import { IExpressionEntry } from "./helpers/IExpressionEntry";
21
- import { OptionPredicator } from "./helpers/OptionPredicator";
22
- import { UnionExplorer } from "./helpers/UnionExplorer";
23
- import { check_array } from "./internal/check_array";
24
- import { check_array_length } from "./internal/check_array_length";
25
- import { check_bigint } from "./internal/check_bigint";
26
- import { check_native } from "./internal/check_native";
27
- import { check_number } from "./internal/check_number";
28
- import { check_string } from "./internal/check_string";
29
- import { check_template } from "./internal/check_template";
30
- import { check_union_tuple } from "./internal/check_union_tuple";
31
- import { decode_union_object } from "./internal/decode_union_object";
32
-
33
- export namespace CheckerProgrammer {
34
- export interface IConfig {
35
- functors: string;
36
- unioners: string;
37
- path: boolean;
38
- trace: boolean;
39
- equals: boolean;
40
- numeric: boolean;
41
- combiner: IConfig.Combiner;
42
- decoder?: FeatureProgrammer.Decoder<Metadata, ts.Expression>;
43
- atomist: (
44
- explore: IExplore,
45
- ) => (check: ICheckEntry) => (input: ts.Expression) => ts.Expression;
46
- joiner: IConfig.IJoiner;
47
- success: ts.Expression;
48
- }
49
- export namespace IConfig {
50
- export interface Combiner {
51
- (explorer: IExplore): {
52
- (logic: "and" | "or"): {
53
- (
54
- input: ts.Expression,
55
- binaries: IBinary[],
56
- expected: string,
57
- ): ts.Expression;
58
- };
59
- };
60
- }
61
- export interface IJoiner {
62
- object(
63
- input: ts.Expression,
64
- entries: IExpressionEntry[],
65
- ): ts.Expression;
66
- array(input: ts.Expression, arrow: ts.ArrowFunction): ts.Expression;
67
- tuple?(exprs: ts.Expression[]): ts.Expression;
68
-
69
- failure(
70
- value: ts.Expression,
71
- expected: string,
72
- explore?: FeatureProgrammer.IExplore,
73
- ): ts.Expression;
74
- is?(expression: ts.Expression): ts.Expression;
75
- required?(exp: ts.Expression): ts.Expression;
76
- full?: (
77
- condition: ts.Expression,
78
- ) => (
79
- input: ts.Expression,
80
- expected: string,
81
- explore: IExplore,
82
- ) => ts.Expression;
83
- }
84
- }
85
- export import IExplore = FeatureProgrammer.IExplore;
86
-
87
- export interface IBinary {
88
- expression: ts.Expression;
89
- combined: boolean;
90
- }
91
-
92
- /* -----------------------------------------------------------
93
- GENERATORS
94
- ----------------------------------------------------------- */
95
- export function generate(
96
- project: IProject,
97
- config: IConfig,
98
- importer: FunctionImporter,
99
- addition?: () => ts.Statement[],
100
- ) {
101
- return FeatureProgrammer.generate(
102
- project,
103
- CONFIG(project, config, importer),
104
- importer,
105
- () => (addition ? (addition ? addition() : []) : undefined),
106
- );
107
- }
108
-
109
- export const generate_functors = (
110
- project: IProject,
111
- config: IConfig,
112
- importer: FunctionImporter,
113
- ) =>
114
- FeatureProgrammer.generate_functors(CONFIG(project, config, importer))(
115
- importer,
116
- );
117
-
118
- export const generate_unioners = (
119
- project: IProject,
120
- config: IConfig,
121
- importer: FunctionImporter,
122
- ) =>
123
- FeatureProgrammer.generate_unioners(
124
- CONFIG(project, { ...config, numeric: false }, importer),
125
- )(importer);
126
-
127
- function CONFIG(
128
- project: IProject,
129
- config: IConfig,
130
- importer: FunctionImporter,
131
- ): FeatureProgrammer.IConfig {
132
- const output: FeatureProgrammer.IConfig = {
133
- types: {
134
- input: () => TypeFactory.keyword("any"),
135
- output: (type, name) =>
136
- ts.factory.createTypePredicateNode(
137
- undefined,
138
- "input",
139
- ts.factory.createTypeReferenceNode(
140
- name ??
141
- TypeFactory.getFullName(project.checker, type),
142
- ),
143
- ),
144
- },
145
- trace: config.trace,
146
- path: config.path,
147
- functors: config.functors,
148
- unioners: config.unioners,
149
- initializer: ({ checker }, type) => {
150
- const collection: MetadataCollection = new MetadataCollection();
151
- const meta: Metadata = MetadataFactory.generate(
152
- checker,
153
- collection,
154
- type,
155
- {
156
- resolve: false,
157
- constant: true,
158
- },
159
- );
160
- return [collection, meta];
161
- },
162
- decoder: config.decoder || decode(project, config, importer),
163
- objector: {
164
- checker: config.decoder || decode(project, config, importer),
165
- decoder: decode_object(config)(importer),
166
- joiner: config.joiner.object,
167
- unionizer: config.equals
168
- ? decode_union_object(decode_object(config)(importer))(
169
- (input, obj, explore) =>
170
- decode_object(config)(importer)(input, obj, {
171
- ...explore,
172
- tracable: true,
173
- }),
174
- )(config.joiner.is || ((expr) => expr))(
175
- (value, expected) =>
176
- ts.factory.createReturnStatement(
177
- config.joiner.failure(value, expected),
178
- ),
179
- )
180
- : (input, targets, explore) =>
181
- config.combiner(explore)("or")(
182
- input,
183
- targets.map((obj) => ({
184
- expression: decode_object(config)(importer)(
185
- input,
186
- obj,
187
- explore,
188
- ),
189
- combined: true,
190
- })),
191
- `(${targets.map((t) => t.name).join(" | ")})`,
192
- ),
193
- failure: (value, expected) =>
194
- ts.factory.createReturnStatement(
195
- config.joiner.failure(value, expected),
196
- ),
197
- is: config.joiner.is,
198
- required: config.joiner.required,
199
- full: config.joiner.full,
200
- type: TypeFactory.keyword("boolean"),
201
- },
202
- };
203
- if (config.numeric === true)
204
- output.generator = {
205
- unioners: FeatureProgrammer.generate_unioners(
206
- CONFIG(project, { ...config, numeric: false }, importer),
207
- )(importer),
208
- };
209
- return output;
210
- }
211
-
212
- /* -----------------------------------------------------------
213
- DECODERS
214
- ----------------------------------------------------------- */
215
- export function decode(
216
- project: IProject,
217
- config: IConfig,
218
- importer: FunctionImporter,
219
- ): (
220
- input: ts.Expression,
221
- meta: Metadata,
222
- explore: IExplore,
223
- tags: IMetadataTag[],
224
- jsDocTags: ts.JSDocTagInfo[],
225
- ) => ts.Expression;
226
-
227
- /**
228
- * @internal
229
- */
230
- export function decode(
231
- project: IProject,
232
- config: IConfig,
233
- importer: FunctionImporter,
234
- checkTupleLength: boolean,
235
- ): (
236
- input: ts.Expression,
237
- meta: Metadata,
238
- explore: IExplore,
239
- tags: IMetadataTag[],
240
- jsDocTags: ts.JSDocTagInfo[],
241
- ) => ts.Expression;
242
-
243
- /**
244
- * @internal
245
- */
246
- export function decode(
247
- project: IProject,
248
- config: IConfig,
249
- importer: FunctionImporter,
250
- ) {
251
- return function (
252
- input: ts.Expression,
253
- meta: Metadata,
254
- explore: IExplore,
255
- metaTags: IMetadataTag[],
256
- jsDocTags: ts.JSDocTagInfo[],
257
- ): ts.Expression {
258
- if (meta.any) return config.success;
259
-
260
- const top: IBinary[] = [];
261
- const binaries: IBinary[] = [];
262
- const add = create_add(binaries)(input);
263
- const getConstantValue = (
264
- value: number | string | bigint | boolean,
265
- ) =>
266
- typeof value === "string"
267
- ? ts.factory.createStringLiteral(value)
268
- : ts.factory.createIdentifier(value.toString());
269
-
270
- //----
271
- // CHECK OPTIONAL
272
- //----
273
- // @todo -> should be elaborated
274
- const checkOptional: boolean = meta.empty() || meta.isUnionBucket();
275
-
276
- // NULLABLE
277
- if (
278
- checkOptional ||
279
- meta.nullable
280
- // || (meta.objects.length && meta.size() !== meta.objects.length)
281
- )
282
- (meta.nullable ? add : create_add(top)(input))(
283
- meta.nullable,
284
- ValueFactory.NULL(),
285
- );
286
-
287
- // UNDEFINDABLE
288
- if (checkOptional || !meta.required)
289
- (meta.required ? create_add(top)(input) : add)(
290
- !meta.required,
291
- ValueFactory.UNDEFINED(),
292
- );
293
-
294
- // FUNCTIONAL
295
- if (meta.functional === true)
296
- if (
297
- OptionPredicator.functional(project.options) ||
298
- meta.size() !== 1
299
- )
300
- add(
301
- true,
302
- ts.factory.createStringLiteral("function"),
303
- ValueFactory.TYPEOF(input),
304
- );
305
- else
306
- binaries.push({
307
- combined: false,
308
- expression: config.success,
309
- });
310
-
311
- //----
312
- // VALUES
313
- //----
314
- // CONSTANT VALUES
315
- for (const constant of meta.constants)
316
- if (AtomicPredicator.constant(meta)(constant.type))
317
- for (const val of constant.values)
318
- add(true, getConstantValue(val));
319
-
320
- // ATOMIC VALUES
321
- for (const type of meta.atomics)
322
- if (AtomicPredicator.atomic(meta)(type) === false) continue;
323
- else if (type === "number")
324
- binaries.push({
325
- expression: config.atomist(explore)(
326
- check_number(project, config.numeric)(importer)(
327
- metaTags,
328
- )(jsDocTags)(input),
329
- )(input),
330
- combined: false,
331
- });
332
- else if (type === "bigint")
333
- binaries.push({
334
- expression: config.atomist(explore)(
335
- check_bigint(importer)(metaTags)(jsDocTags)(input),
336
- )(input),
337
- combined: false,
338
- });
339
- else if (type === "string")
340
- binaries.push({
341
- expression: config.atomist(explore)(
342
- check_string(importer)(metaTags)(jsDocTags)(input),
343
- )(input),
344
- combined: false,
345
- });
346
- else
347
- add(
348
- true,
349
- ts.factory.createStringLiteral(type),
350
- ValueFactory.TYPEOF(input),
351
- );
352
-
353
- // TEMPLATE LITERAL VALUES
354
- if (meta.templates.length)
355
- if (AtomicPredicator.template(meta))
356
- binaries.push({
357
- expression: config.atomist(explore)(
358
- check_template(importer)(metaTags)(jsDocTags)(
359
- meta.templates,
360
- )(input),
361
- )(input),
362
- combined: false,
363
- });
364
-
365
- // NATIVE CLASSES
366
- for (const native of meta.natives)
367
- binaries.push({
368
- expression: check_native(native)(input),
369
- combined: false,
370
- });
371
-
372
- //----
373
- // INSTANCES
374
- //----
375
- interface IInstance {
376
- pre: ts.Expression;
377
- body: ts.Expression | null;
378
- expected: string;
379
- }
380
- const instances: IInstance[] = [];
381
- const prepare =
382
- (pre: ts.Expression, expected: string) =>
383
- (body: ts.Expression | null) =>
384
- instances.push({
385
- pre,
386
- expected,
387
- body,
388
- });
389
-
390
- // SETS
391
- if (meta.sets.length) {
392
- const install = prepare(
393
- check_native("Set")(input),
394
- meta.sets
395
- .map((elem) => `Set<${elem.getName()}>`)
396
- .join(" | "),
397
- );
398
- if (meta.sets.some((elem) => elem.any)) install(null);
399
- else
400
- install(
401
- explore_sets(project, config, importer)(
402
- input,
403
- meta.sets,
404
- {
405
- ...explore,
406
- from: "array",
407
- },
408
- [],
409
- [],
410
- ),
411
- );
412
- }
413
-
414
- // MAPS
415
- if (meta.maps.length) {
416
- const install = prepare(
417
- check_native("Map")(input),
418
- meta.maps
419
- .map(({ key, value }) => `Map<${key}, ${value}>`)
420
- .join(" | "),
421
- );
422
- if (meta.maps.some((elem) => elem.key.any && elem.value.any))
423
- install(null);
424
- else
425
- install(
426
- explore_maps(project, config, importer)(
427
- input,
428
- meta.maps.map((m) => [m.key, m.value]),
429
- {
430
- ...explore,
431
- from: "array",
432
- },
433
- [],
434
- [],
435
- ),
436
- );
437
- }
438
-
439
- // ARRAYS AND TUPLES
440
- if (meta.tuples.length + meta.arrays.length > 0) {
441
- const install = prepare(
442
- config.atomist(explore)(
443
- check_array(importer)(
444
- meta.tuples.length === 0 ? metaTags : [],
445
- )(jsDocTags)(input),
446
- )(input),
447
- [...meta.tuples, ...meta.arrays]
448
- .map((elem) =>
449
- Array.isArray(elem)
450
- ? `[${elem
451
- .map((elem) => elem.getName())
452
- .join(", ")}]`
453
- : `Array<${elem.getName()}>`,
454
- )
455
- .join(" | "),
456
- );
457
- if (meta.arrays.length === 0)
458
- install(
459
- explore_tuples(project, config, importer)(
460
- input,
461
- meta.tuples,
462
- {
463
- ...explore,
464
- from: "array",
465
- },
466
- metaTags,
467
- jsDocTags,
468
- ),
469
- );
470
- else if (meta.arrays.some((elem) => elem.any)) install(null);
471
- else if (meta.tuples.length === 0)
472
- // ARRAY ONLY
473
- install(
474
- explore_arrays(project, config, importer)(
475
- input,
476
- meta.arrays,
477
- {
478
- ...explore,
479
- from: "array",
480
- },
481
- metaTags,
482
- jsDocTags,
483
- ),
484
- );
485
- else
486
- install(
487
- explore_arrays_and_tuples(project, config, importer)(
488
- input,
489
- [...meta.tuples, ...meta.arrays],
490
- explore,
491
- metaTags,
492
- jsDocTags,
493
- ),
494
- );
495
- }
496
-
497
- // OBJECT
498
- if (meta.objects.length > 0)
499
- prepare(
500
- ExpressionFactory.isObject(input, {
501
- checkNull: true,
502
- checkArray: meta.objects.some((obj) =>
503
- obj.properties.every(
504
- (prop) =>
505
- !prop.key.isSoleLiteral() ||
506
- !prop.value.required,
507
- ),
508
- ),
509
- }),
510
- meta.objects.map((obj) => `${obj.name}`).join(" | "),
511
- )(
512
- explore_objects(config)(importer)(input, meta, {
513
- ...explore,
514
- from: "object",
515
- }),
516
- );
517
-
518
- if (instances.length) {
519
- const transformer =
520
- (
521
- merger: (
522
- x: ts.Expression,
523
- y: ts.Expression,
524
- ) => ts.Expression,
525
- ) =>
526
- (ins: IInstance) =>
527
- ins.body
528
- ? {
529
- expression: merger(ins.pre, ins.body),
530
- combined: true,
531
- }
532
- : {
533
- expression: ins.pre,
534
- combined: false,
535
- };
536
- if (instances.length === 1)
537
- binaries.push(
538
- transformer((pre, body) =>
539
- config.combiner(explore)("and")(
540
- input,
541
- [pre, body].map((expression) => ({
542
- expression,
543
- combined: expression !== pre,
544
- })),
545
- meta.getName(),
546
- ),
547
- )(instances[0]!),
548
- );
549
- else
550
- binaries.push({
551
- expression: config.combiner(explore)("or")(
552
- input,
553
- instances.map(
554
- transformer(ts.factory.createLogicalAnd),
555
- ),
556
- meta.getName(),
557
- ),
558
- combined: true,
559
- });
560
- }
561
-
562
- //----
563
- // COMBINE CONDITIONS
564
- //----
565
- return top.length && binaries.length
566
- ? config.combiner(explore)("and")(
567
- input,
568
- [
569
- ...top,
570
- {
571
- expression: config.combiner(explore)("or")(
572
- input,
573
- binaries,
574
- meta.getName(),
575
- ),
576
- combined: true,
577
- },
578
- ],
579
- meta.getName(),
580
- )
581
- : binaries.length
582
- ? config.combiner(explore)("or")(
583
- input,
584
- binaries,
585
- meta.getName(),
586
- )
587
- : config.success;
588
- };
589
- }
590
-
591
- export function decode_tuple(
592
- project: IProject,
593
- config: IConfig,
594
- importer: FunctionImporter,
595
- checkLength: boolean,
596
- ) {
597
- return function (
598
- input: ts.Expression,
599
- tuple: Array<Metadata>,
600
- explore: IExplore,
601
- tagList: IMetadataTag[],
602
- jsDocTags: ts.JSDocTagInfo[],
603
- ): ts.Expression {
604
- const binaries: ts.Expression[] = tuple
605
- .filter((meta) => meta.rest === null)
606
- .map((meta, index) =>
607
- decode(project, config, importer)(
608
- ts.factory.createElementAccessExpression(input, index),
609
- meta,
610
- {
611
- ...explore,
612
- from: "array",
613
- postfix: explore.postfix.length
614
- ? `${explore.postfix.slice(0, -1)}[${index}]"`
615
- : `[${index}]`,
616
- },
617
- tagList,
618
- jsDocTags,
619
- ),
620
- );
621
- const rest: ts.Expression | null =
622
- tuple.length && tuple[tuple.length - 1]!.rest !== null
623
- ? decode(project, config, importer, false)(
624
- ts.factory.createCallExpression(
625
- IdentifierFactory.join(input, "slice"),
626
- undefined,
627
- [
628
- ts.factory.createNumericLiteral(
629
- tuple.length - 1,
630
- ),
631
- ],
632
- ),
633
- (() => {
634
- const wrapper: Metadata = Metadata.initialize();
635
- wrapper.arrays.push(
636
- tuple[tuple.length - 1]!.rest!,
637
- );
638
- return wrapper;
639
- })(),
640
- {
641
- ...explore,
642
- start: tuple.length - 1,
643
- },
644
- tagList,
645
- jsDocTags,
646
- )
647
- : null;
648
-
649
- return config.combiner(explore)("and")(
650
- input,
651
- [
652
- ...(checkLength && rest === null
653
- ? [
654
- {
655
- combined: false,
656
- expression: ts.factory.createStrictEquality(
657
- ts.factory.createPropertyAccessExpression(
658
- input,
659
- "length",
660
- ),
661
- ts.factory.createNumericLiteral(
662
- tuple.length,
663
- ),
664
- ),
665
- },
666
- ]
667
- : []),
668
- ...(config.joiner.tuple
669
- ? [
670
- {
671
- expression: config.joiner.tuple(binaries),
672
- combined: true,
673
- },
674
- ]
675
- : binaries.map((expression) => ({
676
- expression,
677
- combined: true,
678
- }))),
679
- ...(rest !== null
680
- ? [
681
- {
682
- expression: rest,
683
- combined: true,
684
- },
685
- ]
686
- : []),
687
- ],
688
- `[${tuple.map((t) => t.getName()).join(", ")}]`,
689
- );
690
- };
691
- }
692
-
693
- function decode_array(
694
- project: IProject,
695
- config: IConfig,
696
- importer: FunctionImporter,
697
- checkTupleLength: boolean,
698
- ) {
699
- return FeatureProgrammer.decode_array(
700
- {
701
- trace: config.trace,
702
- path: config.path,
703
- decoder: decode(project, config, importer, checkTupleLength),
704
- },
705
- importer,
706
- config.joiner.array,
707
- );
708
- }
709
-
710
- export const decode_object =
711
- (config: IConfig) => (importer: FunctionImporter) => {
712
- const func = FeatureProgrammer.decode_object(config)(importer);
713
- return function (
714
- input: ts.Expression,
715
- obj: MetadataObject,
716
- explore: IExplore,
717
- ) {
718
- obj.validated = true;
719
- return func(input, obj, explore);
720
- };
721
- };
722
-
723
- const explore_sets = (
724
- project: IProject,
725
- config: IConfig,
726
- importer: FunctionImporter,
727
- ) =>
728
- UnionExplorer.set({
729
- checker: decode(project, config, importer),
730
- decoder: decode_array(project, config, importer, true),
731
- empty: config.success,
732
- success: config.success,
733
- failure: (input, expected, explore) =>
734
- ts.factory.createReturnStatement(
735
- config.joiner.failure(input, expected, explore),
736
- ),
737
- });
738
-
739
- const explore_maps = (
740
- project: IProject,
741
- config: IConfig,
742
- importer: FunctionImporter,
743
- ) =>
744
- UnionExplorer.map({
745
- checker: (input, entry, explore) => {
746
- const func = decode(project, config, importer);
747
- return ts.factory.createLogicalAnd(
748
- func(
749
- ts.factory.createElementAccessExpression(input, 0),
750
- entry[0],
751
- { ...explore, postfix: `${explore.postfix}[0]` },
752
- [],
753
- [],
754
- ),
755
- func(
756
- ts.factory.createElementAccessExpression(input, 1),
757
- entry[1],
758
- { ...explore, postfix: `${explore.postfix}[1]` },
759
- [],
760
- [],
761
- ),
762
- );
763
- },
764
- decoder: (input, target, explore) =>
765
- decode_array(project, config, importer, false)(
766
- input,
767
- Metadata.create({
768
- any: false,
769
- nullable: false,
770
- required: true,
771
- functional: false,
772
- resolved: null,
773
- constants: [],
774
- atomics: [],
775
- templates: [],
776
- rest: null,
777
- arrays: [],
778
- tuples: [target],
779
- objects: [],
780
- natives: [],
781
- sets: [],
782
- maps: [],
783
- }),
784
- explore,
785
- [],
786
- [],
787
- ),
788
- empty: config.success,
789
- success: config.success,
790
- failure: (input, expected, explore) =>
791
- ts.factory.createReturnStatement(
792
- config.joiner.failure(input, expected, explore),
793
- ),
794
- });
795
-
796
- const explore_tuples = (
797
- project: IProject,
798
- config: IConfig,
799
- importer: FunctionImporter,
800
- ) =>
801
- UnionExplorer.tuple({
802
- checker: check_union_tuple(project, config, importer),
803
- decoder: decode_tuple(project, config, importer, true),
804
- empty: config.success,
805
- success: config.success,
806
- failure: (input, expected, explore) =>
807
- ts.factory.createReturnStatement(
808
- config.joiner.failure(input, expected, explore),
809
- ),
810
- });
811
-
812
- const explore_arrays = (
813
- project: IProject,
814
- config: IConfig,
815
- importer: FunctionImporter,
816
- ) =>
817
- UnionExplorer.array({
818
- checker: decode(project, config, importer),
819
- decoder: decode_array(project, config, importer, true),
820
- empty: config.success,
821
- success: config.success,
822
- failure: (input, expected, explore) =>
823
- ts.factory.createReturnStatement(
824
- config.joiner.failure(input, expected, explore),
825
- ),
826
- });
827
-
828
- const explore_arrays_and_tuples = (
829
- project: IProject,
830
- config: IConfig,
831
- importer: FunctionImporter,
832
- ) =>
833
- UnionExplorer.array_or_tuple({
834
- checker: (front, target, explore, tags, jsDocTags, array) =>
835
- Array.isArray(target)
836
- ? check_union_tuple(project, config, importer)(
837
- front,
838
- target,
839
- explore,
840
- tags,
841
- jsDocTags,
842
- array,
843
- )
844
- : config.atomist(explore)({
845
- expression: decode(project, config, importer)(
846
- front,
847
- target,
848
- explore,
849
- tags,
850
- jsDocTags,
851
- ),
852
- tags: check_array_length(tags)(array),
853
- })(array),
854
- decoder: (input, target, explore, tags, jsDocTags) =>
855
- Array.isArray(target)
856
- ? decode_tuple(project, config, importer, true)(
857
- input,
858
- target,
859
- explore,
860
- tags,
861
- jsDocTags,
862
- )
863
- : decode_array(project, config, importer, true)(
864
- input,
865
- target,
866
- explore,
867
- tags,
868
- jsDocTags,
869
- ),
870
- empty: config.success,
871
- success: config.success,
872
- failure: (input, expected, explore) =>
873
- ts.factory.createReturnStatement(
874
- config.joiner.failure(input, expected, explore),
875
- ),
876
- });
877
-
878
- const explore_objects =
879
- (config: IConfig) => (importer: FunctionImporter) => {
880
- const objector = decode_object(config)(importer);
881
-
882
- return (
883
- input: ts.Expression,
884
- meta: Metadata,
885
- explore: IExplore,
886
- ) => {
887
- if (meta.objects.length === 1)
888
- return objector(input, meta.objects[0]!, explore);
889
-
890
- return ts.factory.createCallExpression(
891
- ts.factory.createIdentifier(
892
- importer.useLocal(
893
- `${config.unioners}${meta.union_index!}`,
894
- ),
895
- ),
896
- undefined,
897
- FeatureProgrammer.get_object_arguments(config)(explore)(
898
- input,
899
- ),
900
- );
901
- };
902
- };
903
- }
904
-
905
- const create_add =
906
- (binaries: CheckerProgrammer.IBinary[]) =>
907
- (defaultInput: ts.Expression) =>
908
- (
909
- exact: boolean,
910
- left: ts.Expression,
911
- right: ts.Expression = defaultInput,
912
- ) => {
913
- const factory = exact
914
- ? ts.factory.createStrictEquality
915
- : ts.factory.createStrictInequality;
916
- binaries.push({
917
- expression: factory(left, right),
918
- combined: false,
919
- });
920
- };
1
+ import ts from "typescript";
2
+
3
+ import { ExpressionFactory } from "../factories/ExpressionFactory";
4
+ import { IdentifierFactory } from "../factories/IdentifierFactory";
5
+ import { MetadataCollection } from "../factories/MetadataCollection";
6
+ import { MetadataFactory } from "../factories/MetadataFactory";
7
+ import { TypeFactory } from "../factories/TypeFactory";
8
+ import { ValueFactory } from "../factories/ValueFactory";
9
+
10
+ import { IMetadataTag } from "../metadata/IMetadataTag";
11
+ import { Metadata } from "../metadata/Metadata";
12
+ import { MetadataObject } from "../metadata/MetadataObject";
13
+
14
+ import { IProject } from "../transformers/IProject";
15
+
16
+ import { FeatureProgrammer } from "./FeatureProgrammer";
17
+ import { AtomicPredicator } from "./helpers/AtomicPredicator";
18
+ import { FunctionImporter } from "./helpers/FunctionImporeter";
19
+ import { ICheckEntry } from "./helpers/ICheckEntry";
20
+ import { IExpressionEntry } from "./helpers/IExpressionEntry";
21
+ import { OptionPredicator } from "./helpers/OptionPredicator";
22
+ import { UnionExplorer } from "./helpers/UnionExplorer";
23
+ import { check_array } from "./internal/check_array";
24
+ import { check_array_length } from "./internal/check_array_length";
25
+ import { check_bigint } from "./internal/check_bigint";
26
+ import { check_native } from "./internal/check_native";
27
+ import { check_number } from "./internal/check_number";
28
+ import { check_string } from "./internal/check_string";
29
+ import { check_template } from "./internal/check_template";
30
+ import { check_union_tuple } from "./internal/check_union_tuple";
31
+ import { decode_union_object } from "./internal/decode_union_object";
32
+
33
+ export namespace CheckerProgrammer {
34
+ export interface IConfig {
35
+ functors: string;
36
+ unioners: string;
37
+ path: boolean;
38
+ trace: boolean;
39
+ equals: boolean;
40
+ numeric: boolean;
41
+ combiner: IConfig.Combiner;
42
+ decoder?: FeatureProgrammer.Decoder<Metadata, ts.Expression>;
43
+ atomist: (
44
+ explore: IExplore,
45
+ ) => (check: ICheckEntry) => (input: ts.Expression) => ts.Expression;
46
+ joiner: IConfig.IJoiner;
47
+ success: ts.Expression;
48
+ }
49
+ export namespace IConfig {
50
+ export interface Combiner {
51
+ (explorer: IExplore): {
52
+ (logic: "and" | "or"): {
53
+ (
54
+ input: ts.Expression,
55
+ binaries: IBinary[],
56
+ expected: string,
57
+ ): ts.Expression;
58
+ };
59
+ };
60
+ }
61
+ export interface IJoiner {
62
+ object(
63
+ input: ts.Expression,
64
+ entries: IExpressionEntry[],
65
+ ): ts.Expression;
66
+ array(input: ts.Expression, arrow: ts.ArrowFunction): ts.Expression;
67
+ tuple?(exprs: ts.Expression[]): ts.Expression;
68
+
69
+ failure(
70
+ value: ts.Expression,
71
+ expected: string,
72
+ explore?: FeatureProgrammer.IExplore,
73
+ ): ts.Expression;
74
+ is?(expression: ts.Expression): ts.Expression;
75
+ required?(exp: ts.Expression): ts.Expression;
76
+ full?: (
77
+ condition: ts.Expression,
78
+ ) => (
79
+ input: ts.Expression,
80
+ expected: string,
81
+ explore: IExplore,
82
+ ) => ts.Expression;
83
+ }
84
+ }
85
+ export import IExplore = FeatureProgrammer.IExplore;
86
+
87
+ export interface IBinary {
88
+ expression: ts.Expression;
89
+ combined: boolean;
90
+ }
91
+
92
+ /* -----------------------------------------------------------
93
+ GENERATORS
94
+ ----------------------------------------------------------- */
95
+ export function generate(
96
+ project: IProject,
97
+ config: IConfig,
98
+ importer: FunctionImporter,
99
+ addition?: () => ts.Statement[],
100
+ ) {
101
+ return FeatureProgrammer.generate(
102
+ project,
103
+ CONFIG(project, config, importer),
104
+ importer,
105
+ () => (addition ? (addition ? addition() : []) : undefined),
106
+ );
107
+ }
108
+
109
+ export const generate_functors = (
110
+ project: IProject,
111
+ config: IConfig,
112
+ importer: FunctionImporter,
113
+ ) =>
114
+ FeatureProgrammer.generate_functors(CONFIG(project, config, importer))(
115
+ importer,
116
+ );
117
+
118
+ export const generate_unioners = (
119
+ project: IProject,
120
+ config: IConfig,
121
+ importer: FunctionImporter,
122
+ ) =>
123
+ FeatureProgrammer.generate_unioners(
124
+ CONFIG(project, { ...config, numeric: false }, importer),
125
+ )(importer);
126
+
127
+ function CONFIG(
128
+ project: IProject,
129
+ config: IConfig,
130
+ importer: FunctionImporter,
131
+ ): FeatureProgrammer.IConfig {
132
+ const output: FeatureProgrammer.IConfig = {
133
+ types: {
134
+ input: () => TypeFactory.keyword("any"),
135
+ output: (type, name) =>
136
+ ts.factory.createTypePredicateNode(
137
+ undefined,
138
+ "input",
139
+ ts.factory.createTypeReferenceNode(
140
+ name ??
141
+ TypeFactory.getFullName(project.checker, type),
142
+ ),
143
+ ),
144
+ },
145
+ trace: config.trace,
146
+ path: config.path,
147
+ functors: config.functors,
148
+ unioners: config.unioners,
149
+ initializer: ({ checker }, type) => {
150
+ const collection: MetadataCollection = new MetadataCollection();
151
+ const meta: Metadata = MetadataFactory.generate(
152
+ checker,
153
+ collection,
154
+ type,
155
+ {
156
+ resolve: false,
157
+ constant: true,
158
+ },
159
+ );
160
+ return [collection, meta];
161
+ },
162
+ decoder: config.decoder || decode(project, config, importer),
163
+ objector: {
164
+ checker: config.decoder || decode(project, config, importer),
165
+ decoder: decode_object(config)(importer),
166
+ joiner: config.joiner.object,
167
+ unionizer: config.equals
168
+ ? decode_union_object(decode_object(config)(importer))(
169
+ (input, obj, explore) =>
170
+ decode_object(config)(importer)(input, obj, {
171
+ ...explore,
172
+ tracable: true,
173
+ }),
174
+ )(config.joiner.is || ((expr) => expr))(
175
+ (value, expected) =>
176
+ ts.factory.createReturnStatement(
177
+ config.joiner.failure(value, expected),
178
+ ),
179
+ )
180
+ : (input, targets, explore) =>
181
+ config.combiner(explore)("or")(
182
+ input,
183
+ targets.map((obj) => ({
184
+ expression: decode_object(config)(importer)(
185
+ input,
186
+ obj,
187
+ explore,
188
+ ),
189
+ combined: true,
190
+ })),
191
+ `(${targets.map((t) => t.name).join(" | ")})`,
192
+ ),
193
+ failure: (value, expected) =>
194
+ ts.factory.createReturnStatement(
195
+ config.joiner.failure(value, expected),
196
+ ),
197
+ is: config.joiner.is,
198
+ required: config.joiner.required,
199
+ full: config.joiner.full,
200
+ type: TypeFactory.keyword("boolean"),
201
+ },
202
+ };
203
+ if (config.numeric === true)
204
+ output.generator = {
205
+ unioners: FeatureProgrammer.generate_unioners(
206
+ CONFIG(project, { ...config, numeric: false }, importer),
207
+ )(importer),
208
+ };
209
+ return output;
210
+ }
211
+
212
+ /* -----------------------------------------------------------
213
+ DECODERS
214
+ ----------------------------------------------------------- */
215
+ export function decode(
216
+ project: IProject,
217
+ config: IConfig,
218
+ importer: FunctionImporter,
219
+ ): (
220
+ input: ts.Expression,
221
+ meta: Metadata,
222
+ explore: IExplore,
223
+ tags: IMetadataTag[],
224
+ jsDocTags: ts.JSDocTagInfo[],
225
+ ) => ts.Expression;
226
+
227
+ /**
228
+ * @internal
229
+ */
230
+ export function decode(
231
+ project: IProject,
232
+ config: IConfig,
233
+ importer: FunctionImporter,
234
+ checkTupleLength: boolean,
235
+ ): (
236
+ input: ts.Expression,
237
+ meta: Metadata,
238
+ explore: IExplore,
239
+ tags: IMetadataTag[],
240
+ jsDocTags: ts.JSDocTagInfo[],
241
+ ) => ts.Expression;
242
+
243
+ /**
244
+ * @internal
245
+ */
246
+ export function decode(
247
+ project: IProject,
248
+ config: IConfig,
249
+ importer: FunctionImporter,
250
+ ) {
251
+ return function (
252
+ input: ts.Expression,
253
+ meta: Metadata,
254
+ explore: IExplore,
255
+ metaTags: IMetadataTag[],
256
+ jsDocTags: ts.JSDocTagInfo[],
257
+ ): ts.Expression {
258
+ if (meta.any) return config.success;
259
+
260
+ const top: IBinary[] = [];
261
+ const binaries: IBinary[] = [];
262
+ const add = create_add(binaries)(input);
263
+ const getConstantValue = (
264
+ value: number | string | bigint | boolean,
265
+ ) =>
266
+ typeof value === "string"
267
+ ? ts.factory.createStringLiteral(value)
268
+ : ts.factory.createIdentifier(value.toString());
269
+
270
+ //----
271
+ // CHECK OPTIONAL
272
+ //----
273
+ // @todo -> should be elaborated
274
+ const checkOptional: boolean = meta.empty() || meta.isUnionBucket();
275
+
276
+ // NULLABLE
277
+ if (
278
+ checkOptional ||
279
+ meta.nullable
280
+ // || (meta.objects.length && meta.size() !== meta.objects.length)
281
+ )
282
+ (meta.nullable ? add : create_add(top)(input))(
283
+ meta.nullable,
284
+ ValueFactory.NULL(),
285
+ );
286
+
287
+ // UNDEFINDABLE
288
+ if (checkOptional || !meta.required)
289
+ (meta.required ? create_add(top)(input) : add)(
290
+ !meta.required,
291
+ ValueFactory.UNDEFINED(),
292
+ );
293
+
294
+ // FUNCTIONAL
295
+ if (meta.functional === true)
296
+ if (
297
+ OptionPredicator.functional(project.options) ||
298
+ meta.size() !== 1
299
+ )
300
+ add(
301
+ true,
302
+ ts.factory.createStringLiteral("function"),
303
+ ValueFactory.TYPEOF(input),
304
+ );
305
+ else
306
+ binaries.push({
307
+ combined: false,
308
+ expression: config.success,
309
+ });
310
+
311
+ //----
312
+ // VALUES
313
+ //----
314
+ // CONSTANT VALUES
315
+ for (const constant of meta.constants)
316
+ if (AtomicPredicator.constant(meta)(constant.type))
317
+ for (const val of constant.values)
318
+ add(true, getConstantValue(val));
319
+
320
+ // ATOMIC VALUES
321
+ for (const type of meta.atomics)
322
+ if (AtomicPredicator.atomic(meta)(type) === false) continue;
323
+ else if (type === "number")
324
+ binaries.push({
325
+ expression: config.atomist(explore)(
326
+ check_number(project, config.numeric)(importer)(
327
+ metaTags,
328
+ )(jsDocTags)(input),
329
+ )(input),
330
+ combined: false,
331
+ });
332
+ else if (type === "bigint")
333
+ binaries.push({
334
+ expression: config.atomist(explore)(
335
+ check_bigint(importer)(metaTags)(jsDocTags)(input),
336
+ )(input),
337
+ combined: false,
338
+ });
339
+ else if (type === "string")
340
+ binaries.push({
341
+ expression: config.atomist(explore)(
342
+ check_string(importer)(metaTags)(jsDocTags)(input),
343
+ )(input),
344
+ combined: false,
345
+ });
346
+ else
347
+ add(
348
+ true,
349
+ ts.factory.createStringLiteral(type),
350
+ ValueFactory.TYPEOF(input),
351
+ );
352
+
353
+ // TEMPLATE LITERAL VALUES
354
+ if (meta.templates.length)
355
+ if (AtomicPredicator.template(meta))
356
+ binaries.push({
357
+ expression: config.atomist(explore)(
358
+ check_template(importer)(metaTags)(jsDocTags)(
359
+ meta.templates,
360
+ )(input),
361
+ )(input),
362
+ combined: false,
363
+ });
364
+
365
+ // NATIVE CLASSES
366
+ for (const native of meta.natives)
367
+ binaries.push({
368
+ expression: check_native(native)(input),
369
+ combined: false,
370
+ });
371
+
372
+ //----
373
+ // INSTANCES
374
+ //----
375
+ interface IInstance {
376
+ pre: ts.Expression;
377
+ body: ts.Expression | null;
378
+ expected: string;
379
+ }
380
+ const instances: IInstance[] = [];
381
+ const prepare =
382
+ (pre: ts.Expression, expected: string) =>
383
+ (body: ts.Expression | null) =>
384
+ instances.push({
385
+ pre,
386
+ expected,
387
+ body,
388
+ });
389
+
390
+ // SETS
391
+ if (meta.sets.length) {
392
+ const install = prepare(
393
+ check_native("Set")(input),
394
+ meta.sets
395
+ .map((elem) => `Set<${elem.getName()}>`)
396
+ .join(" | "),
397
+ );
398
+ if (meta.sets.some((elem) => elem.any)) install(null);
399
+ else
400
+ install(
401
+ explore_sets(project, config, importer)(
402
+ input,
403
+ meta.sets,
404
+ {
405
+ ...explore,
406
+ from: "array",
407
+ },
408
+ [],
409
+ [],
410
+ ),
411
+ );
412
+ }
413
+
414
+ // MAPS
415
+ if (meta.maps.length) {
416
+ const install = prepare(
417
+ check_native("Map")(input),
418
+ meta.maps
419
+ .map(({ key, value }) => `Map<${key}, ${value}>`)
420
+ .join(" | "),
421
+ );
422
+ if (meta.maps.some((elem) => elem.key.any && elem.value.any))
423
+ install(null);
424
+ else
425
+ install(
426
+ explore_maps(project, config, importer)(
427
+ input,
428
+ meta.maps.map((m) => [m.key, m.value]),
429
+ {
430
+ ...explore,
431
+ from: "array",
432
+ },
433
+ [],
434
+ [],
435
+ ),
436
+ );
437
+ }
438
+
439
+ // ARRAYS AND TUPLES
440
+ if (meta.tuples.length + meta.arrays.length > 0) {
441
+ const install = prepare(
442
+ config.atomist(explore)(
443
+ check_array(importer)(
444
+ meta.tuples.length === 0 ? metaTags : [],
445
+ )(jsDocTags)(input),
446
+ )(input),
447
+ [...meta.tuples, ...meta.arrays]
448
+ .map((elem) =>
449
+ Array.isArray(elem)
450
+ ? `[${elem
451
+ .map((elem) => elem.getName())
452
+ .join(", ")}]`
453
+ : `Array<${elem.getName()}>`,
454
+ )
455
+ .join(" | "),
456
+ );
457
+ if (meta.arrays.length === 0)
458
+ install(
459
+ explore_tuples(project, config, importer)(
460
+ input,
461
+ meta.tuples,
462
+ {
463
+ ...explore,
464
+ from: "array",
465
+ },
466
+ metaTags,
467
+ jsDocTags,
468
+ ),
469
+ );
470
+ else if (meta.arrays.some((elem) => elem.any)) install(null);
471
+ else if (meta.tuples.length === 0)
472
+ // ARRAY ONLY
473
+ install(
474
+ explore_arrays(project, config, importer)(
475
+ input,
476
+ meta.arrays,
477
+ {
478
+ ...explore,
479
+ from: "array",
480
+ },
481
+ metaTags,
482
+ jsDocTags,
483
+ ),
484
+ );
485
+ else
486
+ install(
487
+ explore_arrays_and_tuples(project, config, importer)(
488
+ input,
489
+ [...meta.tuples, ...meta.arrays],
490
+ explore,
491
+ metaTags,
492
+ jsDocTags,
493
+ ),
494
+ );
495
+ }
496
+
497
+ // OBJECT
498
+ if (meta.objects.length > 0)
499
+ prepare(
500
+ ExpressionFactory.isObject(input, {
501
+ checkNull: true,
502
+ checkArray: meta.objects.some((obj) =>
503
+ obj.properties.every(
504
+ (prop) =>
505
+ !prop.key.isSoleLiteral() ||
506
+ !prop.value.required,
507
+ ),
508
+ ),
509
+ }),
510
+ meta.objects.map((obj) => `${obj.name}`).join(" | "),
511
+ )(
512
+ explore_objects(config)(importer)(input, meta, {
513
+ ...explore,
514
+ from: "object",
515
+ }),
516
+ );
517
+
518
+ if (instances.length) {
519
+ const transformer =
520
+ (
521
+ merger: (
522
+ x: ts.Expression,
523
+ y: ts.Expression,
524
+ ) => ts.Expression,
525
+ ) =>
526
+ (ins: IInstance) =>
527
+ ins.body
528
+ ? {
529
+ expression: merger(ins.pre, ins.body),
530
+ combined: true,
531
+ }
532
+ : {
533
+ expression: ins.pre,
534
+ combined: false,
535
+ };
536
+ if (instances.length === 1)
537
+ binaries.push(
538
+ transformer((pre, body) =>
539
+ config.combiner(explore)("and")(
540
+ input,
541
+ [pre, body].map((expression) => ({
542
+ expression,
543
+ combined: expression !== pre,
544
+ })),
545
+ meta.getName(),
546
+ ),
547
+ )(instances[0]!),
548
+ );
549
+ else
550
+ binaries.push({
551
+ expression: config.combiner(explore)("or")(
552
+ input,
553
+ instances.map(
554
+ transformer(ts.factory.createLogicalAnd),
555
+ ),
556
+ meta.getName(),
557
+ ),
558
+ combined: true,
559
+ });
560
+ }
561
+
562
+ //----
563
+ // COMBINE CONDITIONS
564
+ //----
565
+ return top.length && binaries.length
566
+ ? config.combiner(explore)("and")(
567
+ input,
568
+ [
569
+ ...top,
570
+ {
571
+ expression: config.combiner(explore)("or")(
572
+ input,
573
+ binaries,
574
+ meta.getName(),
575
+ ),
576
+ combined: true,
577
+ },
578
+ ],
579
+ meta.getName(),
580
+ )
581
+ : binaries.length
582
+ ? config.combiner(explore)("or")(
583
+ input,
584
+ binaries,
585
+ meta.getName(),
586
+ )
587
+ : config.success;
588
+ };
589
+ }
590
+
591
+ export function decode_tuple(
592
+ project: IProject,
593
+ config: IConfig,
594
+ importer: FunctionImporter,
595
+ checkLength: boolean,
596
+ ) {
597
+ return function (
598
+ input: ts.Expression,
599
+ tuple: Array<Metadata>,
600
+ explore: IExplore,
601
+ tagList: IMetadataTag[],
602
+ jsDocTags: ts.JSDocTagInfo[],
603
+ ): ts.Expression {
604
+ const binaries: ts.Expression[] = tuple
605
+ .filter((meta) => meta.rest === null)
606
+ .map((meta, index) =>
607
+ decode(project, config, importer)(
608
+ ts.factory.createElementAccessExpression(input, index),
609
+ meta,
610
+ {
611
+ ...explore,
612
+ from: "array",
613
+ postfix: explore.postfix.length
614
+ ? `${explore.postfix.slice(0, -1)}[${index}]"`
615
+ : `[${index}]`,
616
+ },
617
+ tagList,
618
+ jsDocTags,
619
+ ),
620
+ );
621
+ const rest: ts.Expression | null =
622
+ tuple.length && tuple[tuple.length - 1]!.rest !== null
623
+ ? decode(project, config, importer, false)(
624
+ ts.factory.createCallExpression(
625
+ IdentifierFactory.join(input, "slice"),
626
+ undefined,
627
+ [
628
+ ts.factory.createNumericLiteral(
629
+ tuple.length - 1,
630
+ ),
631
+ ],
632
+ ),
633
+ (() => {
634
+ const wrapper: Metadata = Metadata.initialize();
635
+ wrapper.arrays.push(
636
+ tuple[tuple.length - 1]!.rest!,
637
+ );
638
+ return wrapper;
639
+ })(),
640
+ {
641
+ ...explore,
642
+ start: tuple.length - 1,
643
+ },
644
+ tagList,
645
+ jsDocTags,
646
+ )
647
+ : null;
648
+
649
+ return config.combiner(explore)("and")(
650
+ input,
651
+ [
652
+ ...(checkLength && rest === null
653
+ ? [
654
+ {
655
+ combined: false,
656
+ expression: ts.factory.createStrictEquality(
657
+ ts.factory.createPropertyAccessExpression(
658
+ input,
659
+ "length",
660
+ ),
661
+ ts.factory.createNumericLiteral(
662
+ tuple.length,
663
+ ),
664
+ ),
665
+ },
666
+ ]
667
+ : []),
668
+ ...(config.joiner.tuple
669
+ ? [
670
+ {
671
+ expression: config.joiner.tuple(binaries),
672
+ combined: true,
673
+ },
674
+ ]
675
+ : binaries.map((expression) => ({
676
+ expression,
677
+ combined: true,
678
+ }))),
679
+ ...(rest !== null
680
+ ? [
681
+ {
682
+ expression: rest,
683
+ combined: true,
684
+ },
685
+ ]
686
+ : []),
687
+ ],
688
+ `[${tuple.map((t) => t.getName()).join(", ")}]`,
689
+ );
690
+ };
691
+ }
692
+
693
+ function decode_array(
694
+ project: IProject,
695
+ config: IConfig,
696
+ importer: FunctionImporter,
697
+ checkTupleLength: boolean,
698
+ ) {
699
+ return FeatureProgrammer.decode_array(
700
+ {
701
+ trace: config.trace,
702
+ path: config.path,
703
+ decoder: decode(project, config, importer, checkTupleLength),
704
+ },
705
+ importer,
706
+ config.joiner.array,
707
+ );
708
+ }
709
+
710
+ export const decode_object =
711
+ (config: IConfig) => (importer: FunctionImporter) => {
712
+ const func = FeatureProgrammer.decode_object(config)(importer);
713
+ return function (
714
+ input: ts.Expression,
715
+ obj: MetadataObject,
716
+ explore: IExplore,
717
+ ) {
718
+ obj.validated = true;
719
+ return func(input, obj, explore);
720
+ };
721
+ };
722
+
723
+ const explore_sets = (
724
+ project: IProject,
725
+ config: IConfig,
726
+ importer: FunctionImporter,
727
+ ) =>
728
+ UnionExplorer.set({
729
+ checker: decode(project, config, importer),
730
+ decoder: decode_array(project, config, importer, true),
731
+ empty: config.success,
732
+ success: config.success,
733
+ failure: (input, expected, explore) =>
734
+ ts.factory.createReturnStatement(
735
+ config.joiner.failure(input, expected, explore),
736
+ ),
737
+ });
738
+
739
+ const explore_maps = (
740
+ project: IProject,
741
+ config: IConfig,
742
+ importer: FunctionImporter,
743
+ ) =>
744
+ UnionExplorer.map({
745
+ checker: (input, entry, explore) => {
746
+ const func = decode(project, config, importer);
747
+ return ts.factory.createLogicalAnd(
748
+ func(
749
+ ts.factory.createElementAccessExpression(input, 0),
750
+ entry[0],
751
+ { ...explore, postfix: `${explore.postfix}[0]` },
752
+ [],
753
+ [],
754
+ ),
755
+ func(
756
+ ts.factory.createElementAccessExpression(input, 1),
757
+ entry[1],
758
+ { ...explore, postfix: `${explore.postfix}[1]` },
759
+ [],
760
+ [],
761
+ ),
762
+ );
763
+ },
764
+ decoder: (input, target, explore) =>
765
+ decode_array(project, config, importer, false)(
766
+ input,
767
+ Metadata.create({
768
+ any: false,
769
+ nullable: false,
770
+ required: true,
771
+ functional: false,
772
+ resolved: null,
773
+ constants: [],
774
+ atomics: [],
775
+ templates: [],
776
+ rest: null,
777
+ arrays: [],
778
+ tuples: [target],
779
+ objects: [],
780
+ natives: [],
781
+ sets: [],
782
+ maps: [],
783
+ }),
784
+ explore,
785
+ [],
786
+ [],
787
+ ),
788
+ empty: config.success,
789
+ success: config.success,
790
+ failure: (input, expected, explore) =>
791
+ ts.factory.createReturnStatement(
792
+ config.joiner.failure(input, expected, explore),
793
+ ),
794
+ });
795
+
796
+ const explore_tuples = (
797
+ project: IProject,
798
+ config: IConfig,
799
+ importer: FunctionImporter,
800
+ ) =>
801
+ UnionExplorer.tuple({
802
+ checker: check_union_tuple(project, config, importer),
803
+ decoder: decode_tuple(project, config, importer, true),
804
+ empty: config.success,
805
+ success: config.success,
806
+ failure: (input, expected, explore) =>
807
+ ts.factory.createReturnStatement(
808
+ config.joiner.failure(input, expected, explore),
809
+ ),
810
+ });
811
+
812
+ const explore_arrays = (
813
+ project: IProject,
814
+ config: IConfig,
815
+ importer: FunctionImporter,
816
+ ) =>
817
+ UnionExplorer.array({
818
+ checker: decode(project, config, importer),
819
+ decoder: decode_array(project, config, importer, true),
820
+ empty: config.success,
821
+ success: config.success,
822
+ failure: (input, expected, explore) =>
823
+ ts.factory.createReturnStatement(
824
+ config.joiner.failure(input, expected, explore),
825
+ ),
826
+ });
827
+
828
+ const explore_arrays_and_tuples = (
829
+ project: IProject,
830
+ config: IConfig,
831
+ importer: FunctionImporter,
832
+ ) =>
833
+ UnionExplorer.array_or_tuple({
834
+ checker: (front, target, explore, tags, jsDocTags, array) =>
835
+ Array.isArray(target)
836
+ ? check_union_tuple(project, config, importer)(
837
+ front,
838
+ target,
839
+ explore,
840
+ tags,
841
+ jsDocTags,
842
+ array,
843
+ )
844
+ : config.atomist(explore)({
845
+ expression: decode(project, config, importer)(
846
+ front,
847
+ target,
848
+ explore,
849
+ tags,
850
+ jsDocTags,
851
+ ),
852
+ tags: check_array_length(tags)(array),
853
+ })(array),
854
+ decoder: (input, target, explore, tags, jsDocTags) =>
855
+ Array.isArray(target)
856
+ ? decode_tuple(project, config, importer, true)(
857
+ input,
858
+ target,
859
+ explore,
860
+ tags,
861
+ jsDocTags,
862
+ )
863
+ : decode_array(project, config, importer, true)(
864
+ input,
865
+ target,
866
+ explore,
867
+ tags,
868
+ jsDocTags,
869
+ ),
870
+ empty: config.success,
871
+ success: config.success,
872
+ failure: (input, expected, explore) =>
873
+ ts.factory.createReturnStatement(
874
+ config.joiner.failure(input, expected, explore),
875
+ ),
876
+ });
877
+
878
+ const explore_objects =
879
+ (config: IConfig) => (importer: FunctionImporter) => {
880
+ const objector = decode_object(config)(importer);
881
+
882
+ return (
883
+ input: ts.Expression,
884
+ meta: Metadata,
885
+ explore: IExplore,
886
+ ) => {
887
+ if (meta.objects.length === 1)
888
+ return objector(input, meta.objects[0]!, explore);
889
+
890
+ return ts.factory.createCallExpression(
891
+ ts.factory.createIdentifier(
892
+ importer.useLocal(
893
+ `${config.unioners}${meta.union_index!}`,
894
+ ),
895
+ ),
896
+ undefined,
897
+ FeatureProgrammer.get_object_arguments(config)(explore)(
898
+ input,
899
+ ),
900
+ );
901
+ };
902
+ };
903
+ }
904
+
905
+ const create_add =
906
+ (binaries: CheckerProgrammer.IBinary[]) =>
907
+ (defaultInput: ts.Expression) =>
908
+ (
909
+ exact: boolean,
910
+ left: ts.Expression,
911
+ right: ts.Expression = defaultInput,
912
+ ) => {
913
+ const factory = exact
914
+ ? ts.factory.createStrictEquality
915
+ : ts.factory.createStrictInequality;
916
+ binaries.push({
917
+ expression: factory(left, right),
918
+ combined: false,
919
+ });
920
+ };