-
Notifications
You must be signed in to change notification settings - Fork 0
/
validators-compiler.ts
105 lines (88 loc) · 2.98 KB
/
validators-compiler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import {IJtdcDialect, JtdNode} from '@jtdc/types';
import {visitJtdNode} from './jtd-visitor';
import {compileJsSource, IFragmentCgNode, template as _} from '@smikhalevski/codegen';
import {createJtdDialect} from '@jtdc/jtd-dialect';
export interface IValidatorCompilerOptions<M, C> {
/**
* If set to `true` then type guards are rendered along with validators.
*
* @see {@link https://www.typescriptlang.org/docs/handbook/2/narrowing.html TypeScript Narrowing}
*/
typeGuardsRendered?: boolean;
/**
* The validator compilation dialect that describes how validators and type guards are compiled.
*/
dialect?: IJtdcDialect<M, C>;
}
/**
* Compiles validators and type guards from the definitions.
*/
export function compileValidators<M, C>(definitions: Record<string, JtdNode<M>>, options?: IValidatorCompilerOptions<M, C>): string {
const opt = {...validatorCompilerOptions, ...options};
const {
typeGuardsRendered,
dialect,
} = opt;
let src = '';
for (const [ref, node] of Object.entries(definitions)) {
src += compileJsSource(dialect.validator(ref, node, (ctx) => compileValidatorBody(node, ctx, dialect)));
if (typeGuardsRendered) {
src += compileJsSource(dialect.typeGuard(ref, node));
}
}
return src;
}
function compileValidatorBody<M, C>(node: JtdNode<M>, ctx: C, dialect: IJtdcDialect<M, C>): IFragmentCgNode {
let frags: Array<IFragmentCgNode> = [];
const createNext = (next: () => void) => (nextCtx: C) => {
const prevFrags = frags;
const prevCtx = ctx;
frags = [];
ctx = nextCtx;
next();
const nextFrag = _(frags);
frags = prevFrags;
ctx = prevCtx;
return nextFrag;
};
visitJtdNode(node, {
ref(node) {
frags.push(dialect.ref(node, ctx));
},
nullable(node, next) {
frags.push(dialect.nullable(node, ctx, createNext(next)));
},
type(node) {
frags.push(dialect.type(node, ctx));
},
enum(node) {
frags.push(dialect.enum(node, ctx));
},
elements(node, next) {
frags.push(dialect.elements(node, ctx, createNext(next)));
},
values(node, next) {
frags.push(dialect.values(node, ctx, createNext(next)));
},
object(node, next) {
frags.push(dialect.object(node, ctx, createNext(next)));
},
property(propKey, propNode, objectNode, next) {
frags.push(dialect.property(propKey, propNode, objectNode, ctx, createNext(next)));
},
optionalProperty(propKey, propNode, objectNode, next) {
frags.push(dialect.optionalProperty(propKey, propNode, objectNode, ctx, createNext(next)));
},
union(node, next) {
frags.push(dialect.union(node, ctx, createNext(next)));
},
mapping(mappingKey, mappingNode, unionNode, next) {
frags.push(dialect.mapping(mappingKey, mappingNode, unionNode, ctx, createNext(next)));
},
});
return _(frags);
}
export const validatorCompilerOptions: Required<IValidatorCompilerOptions<any, any>> = {
typeGuardsRendered: false,
dialect: createJtdDialect(),
};