1 | "use strict";
|
---|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
3 | exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = void 0;
|
---|
4 | const rules_1 = require("../rules");
|
---|
5 | const applicability_1 = require("./applicability");
|
---|
6 | const errors_1 = require("../errors");
|
---|
7 | const codegen_1 = require("../codegen");
|
---|
8 | const util_1 = require("../util");
|
---|
9 | var DataType;
|
---|
10 | (function (DataType) {
|
---|
11 | DataType[DataType["Correct"] = 0] = "Correct";
|
---|
12 | DataType[DataType["Wrong"] = 1] = "Wrong";
|
---|
13 | })(DataType = exports.DataType || (exports.DataType = {}));
|
---|
14 | function getSchemaTypes(schema) {
|
---|
15 | const types = getJSONTypes(schema.type);
|
---|
16 | const hasNull = types.includes("null");
|
---|
17 | if (hasNull) {
|
---|
18 | if (schema.nullable === false)
|
---|
19 | throw new Error("type: null contradicts nullable: false");
|
---|
20 | }
|
---|
21 | else {
|
---|
22 | if (!types.length && schema.nullable !== undefined) {
|
---|
23 | throw new Error('"nullable" cannot be used without "type"');
|
---|
24 | }
|
---|
25 | if (schema.nullable === true)
|
---|
26 | types.push("null");
|
---|
27 | }
|
---|
28 | return types;
|
---|
29 | }
|
---|
30 | exports.getSchemaTypes = getSchemaTypes;
|
---|
31 | function getJSONTypes(ts) {
|
---|
32 | const types = Array.isArray(ts) ? ts : ts ? [ts] : [];
|
---|
33 | if (types.every(rules_1.isJSONType))
|
---|
34 | return types;
|
---|
35 | throw new Error("type must be JSONType or JSONType[]: " + types.join(","));
|
---|
36 | }
|
---|
37 | exports.getJSONTypes = getJSONTypes;
|
---|
38 | function coerceAndCheckDataType(it, types) {
|
---|
39 | const { gen, data, opts } = it;
|
---|
40 | const coerceTo = coerceToTypes(types, opts.coerceTypes);
|
---|
41 | const checkTypes = types.length > 0 &&
|
---|
42 | !(coerceTo.length === 0 && types.length === 1 && applicability_1.schemaHasRulesForType(it, types[0]));
|
---|
43 | if (checkTypes) {
|
---|
44 | const wrongType = checkDataTypes(types, data, opts.strictNumbers, DataType.Wrong);
|
---|
45 | gen.if(wrongType, () => {
|
---|
46 | if (coerceTo.length)
|
---|
47 | coerceData(it, types, coerceTo);
|
---|
48 | else
|
---|
49 | reportTypeError(it);
|
---|
50 | });
|
---|
51 | }
|
---|
52 | return checkTypes;
|
---|
53 | }
|
---|
54 | exports.coerceAndCheckDataType = coerceAndCheckDataType;
|
---|
55 | const COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]);
|
---|
56 | function coerceToTypes(types, coerceTypes) {
|
---|
57 | return coerceTypes
|
---|
58 | ? types.filter((t) => COERCIBLE.has(t) || (coerceTypes === "array" && t === "array"))
|
---|
59 | : [];
|
---|
60 | }
|
---|
61 | function coerceData(it, types, coerceTo) {
|
---|
62 | const { gen, data, opts } = it;
|
---|
63 | const dataType = gen.let("dataType", codegen_1._ `typeof ${data}`);
|
---|
64 | const coerced = gen.let("coerced", codegen_1._ `undefined`);
|
---|
65 | if (opts.coerceTypes === "array") {
|
---|
66 | gen.if(codegen_1._ `${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen
|
---|
67 | .assign(data, codegen_1._ `${data}[0]`)
|
---|
68 | .assign(dataType, codegen_1._ `typeof ${data}`)
|
---|
69 | .if(checkDataTypes(types, data, opts.strictNumbers), () => gen.assign(coerced, data)));
|
---|
70 | }
|
---|
71 | gen.if(codegen_1._ `${coerced} !== undefined`);
|
---|
72 | for (const t of coerceTo) {
|
---|
73 | if (COERCIBLE.has(t) || (t === "array" && opts.coerceTypes === "array")) {
|
---|
74 | coerceSpecificType(t);
|
---|
75 | }
|
---|
76 | }
|
---|
77 | gen.else();
|
---|
78 | reportTypeError(it);
|
---|
79 | gen.endIf();
|
---|
80 | gen.if(codegen_1._ `${coerced} !== undefined`, () => {
|
---|
81 | gen.assign(data, coerced);
|
---|
82 | assignParentData(it, coerced);
|
---|
83 | });
|
---|
84 | function coerceSpecificType(t) {
|
---|
85 | switch (t) {
|
---|
86 | case "string":
|
---|
87 | gen
|
---|
88 | .elseIf(codegen_1._ `${dataType} == "number" || ${dataType} == "boolean"`)
|
---|
89 | .assign(coerced, codegen_1._ `"" + ${data}`)
|
---|
90 | .elseIf(codegen_1._ `${data} === null`)
|
---|
91 | .assign(coerced, codegen_1._ `""`);
|
---|
92 | return;
|
---|
93 | case "number":
|
---|
94 | gen
|
---|
95 | .elseIf(codegen_1._ `${dataType} == "boolean" || ${data} === null
|
---|
96 | || (${dataType} == "string" && ${data} && ${data} == +${data})`)
|
---|
97 | .assign(coerced, codegen_1._ `+${data}`);
|
---|
98 | return;
|
---|
99 | case "integer":
|
---|
100 | gen
|
---|
101 | .elseIf(codegen_1._ `${dataType} === "boolean" || ${data} === null
|
---|
102 | || (${dataType} === "string" && ${data} && ${data} == +${data} && !(${data} % 1))`)
|
---|
103 | .assign(coerced, codegen_1._ `+${data}`);
|
---|
104 | return;
|
---|
105 | case "boolean":
|
---|
106 | gen
|
---|
107 | .elseIf(codegen_1._ `${data} === "false" || ${data} === 0 || ${data} === null`)
|
---|
108 | .assign(coerced, false)
|
---|
109 | .elseIf(codegen_1._ `${data} === "true" || ${data} === 1`)
|
---|
110 | .assign(coerced, true);
|
---|
111 | return;
|
---|
112 | case "null":
|
---|
113 | gen.elseIf(codegen_1._ `${data} === "" || ${data} === 0 || ${data} === false`);
|
---|
114 | gen.assign(coerced, null);
|
---|
115 | return;
|
---|
116 | case "array":
|
---|
117 | gen
|
---|
118 | .elseIf(codegen_1._ `${dataType} === "string" || ${dataType} === "number"
|
---|
119 | || ${dataType} === "boolean" || ${data} === null`)
|
---|
120 | .assign(coerced, codegen_1._ `[${data}]`);
|
---|
121 | }
|
---|
122 | }
|
---|
123 | }
|
---|
124 | function assignParentData({ gen, parentData, parentDataProperty }, expr) {
|
---|
125 | // TODO use gen.property
|
---|
126 | gen.if(codegen_1._ `${parentData} !== undefined`, () => gen.assign(codegen_1._ `${parentData}[${parentDataProperty}]`, expr));
|
---|
127 | }
|
---|
128 | function checkDataType(dataType, data, strictNums, correct = DataType.Correct) {
|
---|
129 | const EQ = correct === DataType.Correct ? codegen_1.operators.EQ : codegen_1.operators.NEQ;
|
---|
130 | let cond;
|
---|
131 | switch (dataType) {
|
---|
132 | case "null":
|
---|
133 | return codegen_1._ `${data} ${EQ} null`;
|
---|
134 | case "array":
|
---|
135 | cond = codegen_1._ `Array.isArray(${data})`;
|
---|
136 | break;
|
---|
137 | case "object":
|
---|
138 | cond = codegen_1._ `${data} && typeof ${data} == "object" && !Array.isArray(${data})`;
|
---|
139 | break;
|
---|
140 | case "integer":
|
---|
141 | cond = numCond(codegen_1._ `!(${data} % 1) && !isNaN(${data})`);
|
---|
142 | break;
|
---|
143 | case "number":
|
---|
144 | cond = numCond();
|
---|
145 | break;
|
---|
146 | default:
|
---|
147 | return codegen_1._ `typeof ${data} ${EQ} ${dataType}`;
|
---|
148 | }
|
---|
149 | return correct === DataType.Correct ? cond : codegen_1.not(cond);
|
---|
150 | function numCond(_cond = codegen_1.nil) {
|
---|
151 | return codegen_1.and(codegen_1._ `typeof ${data} == "number"`, _cond, strictNums ? codegen_1._ `isFinite(${data})` : codegen_1.nil);
|
---|
152 | }
|
---|
153 | }
|
---|
154 | exports.checkDataType = checkDataType;
|
---|
155 | function checkDataTypes(dataTypes, data, strictNums, correct) {
|
---|
156 | if (dataTypes.length === 1) {
|
---|
157 | return checkDataType(dataTypes[0], data, strictNums, correct);
|
---|
158 | }
|
---|
159 | let cond;
|
---|
160 | const types = util_1.toHash(dataTypes);
|
---|
161 | if (types.array && types.object) {
|
---|
162 | const notObj = codegen_1._ `typeof ${data} != "object"`;
|
---|
163 | cond = types.null ? notObj : codegen_1._ `!${data} || ${notObj}`;
|
---|
164 | delete types.null;
|
---|
165 | delete types.array;
|
---|
166 | delete types.object;
|
---|
167 | }
|
---|
168 | else {
|
---|
169 | cond = codegen_1.nil;
|
---|
170 | }
|
---|
171 | if (types.number)
|
---|
172 | delete types.integer;
|
---|
173 | for (const t in types)
|
---|
174 | cond = codegen_1.and(cond, checkDataType(t, data, strictNums, correct));
|
---|
175 | return cond;
|
---|
176 | }
|
---|
177 | exports.checkDataTypes = checkDataTypes;
|
---|
178 | const typeError = {
|
---|
179 | message: ({ schema }) => `must be ${schema}`,
|
---|
180 | params: ({ schema, schemaValue }) => typeof schema == "string" ? codegen_1._ `{type: ${schema}}` : codegen_1._ `{type: ${schemaValue}}`,
|
---|
181 | };
|
---|
182 | function reportTypeError(it) {
|
---|
183 | const cxt = getTypeErrorContext(it);
|
---|
184 | errors_1.reportError(cxt, typeError);
|
---|
185 | }
|
---|
186 | exports.reportTypeError = reportTypeError;
|
---|
187 | function getTypeErrorContext(it) {
|
---|
188 | const { gen, data, schema } = it;
|
---|
189 | const schemaCode = util_1.schemaRefOrVal(it, schema, "type");
|
---|
190 | return {
|
---|
191 | gen,
|
---|
192 | keyword: "type",
|
---|
193 | data,
|
---|
194 | schema: schema.type,
|
---|
195 | schemaCode,
|
---|
196 | schemaValue: schemaCode,
|
---|
197 | parentSchema: schema,
|
---|
198 | params: {},
|
---|
199 | it,
|
---|
200 | };
|
---|
201 | }
|
---|
202 | //# sourceMappingURL=dataType.js.map |
---|