typia 3.8.3 → 3.8.4

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