UNPKG

8.34 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = void 0;
4const rules_1 = require("../rules");
5const applicability_1 = require("./applicability");
6const errors_1 = require("../errors");
7const codegen_1 = require("../codegen");
8const util_1 = require("../util");
9var DataType;
10(function (DataType) {
11 DataType[DataType["Correct"] = 0] = "Correct";
12 DataType[DataType["Wrong"] = 1] = "Wrong";
13})(DataType = exports.DataType || (exports.DataType = {}));
14function 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}
30exports.getSchemaTypes = getSchemaTypes;
31function 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}
37exports.getJSONTypes = getJSONTypes;
38function 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 && (0, 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}
54exports.coerceAndCheckDataType = coerceAndCheckDataType;
55const COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]);
56function coerceToTypes(types, coerceTypes) {
57 return coerceTypes
58 ? types.filter((t) => COERCIBLE.has(t) || (coerceTypes === "array" && t === "array"))
59 : [];
60}
61function coerceData(it, types, coerceTo) {
62 const { gen, data, opts } = it;
63 const dataType = gen.let("dataType", (0, codegen_1._) `typeof ${data}`);
64 const coerced = gen.let("coerced", (0, codegen_1._) `undefined`);
65 if (opts.coerceTypes === "array") {
66 gen.if((0, codegen_1._) `${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen
67 .assign(data, (0, codegen_1._) `${data}[0]`)
68 .assign(dataType, (0, codegen_1._) `typeof ${data}`)
69 .if(checkDataTypes(types, data, opts.strictNumbers), () => gen.assign(coerced, data)));
70 }
71 gen.if((0, 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((0, 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((0, codegen_1._) `${dataType} == "number" || ${dataType} == "boolean"`)
89 .assign(coerced, (0, codegen_1._) `"" + ${data}`)
90 .elseIf((0, codegen_1._) `${data} === null`)
91 .assign(coerced, (0, codegen_1._) `""`);
92 return;
93 case "number":
94 gen
95 .elseIf((0, codegen_1._) `${dataType} == "boolean" || ${data} === null
96 || (${dataType} == "string" && ${data} && ${data} == +${data})`)
97 .assign(coerced, (0, codegen_1._) `+${data}`);
98 return;
99 case "integer":
100 gen
101 .elseIf((0, codegen_1._) `${dataType} === "boolean" || ${data} === null
102 || (${dataType} === "string" && ${data} && ${data} == +${data} && !(${data} % 1))`)
103 .assign(coerced, (0, codegen_1._) `+${data}`);
104 return;
105 case "boolean":
106 gen
107 .elseIf((0, codegen_1._) `${data} === "false" || ${data} === 0 || ${data} === null`)
108 .assign(coerced, false)
109 .elseIf((0, codegen_1._) `${data} === "true" || ${data} === 1`)
110 .assign(coerced, true);
111 return;
112 case "null":
113 gen.elseIf((0, codegen_1._) `${data} === "" || ${data} === 0 || ${data} === false`);
114 gen.assign(coerced, null);
115 return;
116 case "array":
117 gen
118 .elseIf((0, codegen_1._) `${dataType} === "string" || ${dataType} === "number"
119 || ${dataType} === "boolean" || ${data} === null`)
120 .assign(coerced, (0, codegen_1._) `[${data}]`);
121 }
122 }
123}
124function assignParentData({ gen, parentData, parentDataProperty }, expr) {
125 // TODO use gen.property
126 gen.if((0, codegen_1._) `${parentData} !== undefined`, () => gen.assign((0, codegen_1._) `${parentData}[${parentDataProperty}]`, expr));
127}
128function 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 (0, codegen_1._) `${data} ${EQ} null`;
134 case "array":
135 cond = (0, codegen_1._) `Array.isArray(${data})`;
136 break;
137 case "object":
138 cond = (0, codegen_1._) `${data} && typeof ${data} == "object" && !Array.isArray(${data})`;
139 break;
140 case "integer":
141 cond = numCond((0, codegen_1._) `!(${data} % 1) && !isNaN(${data})`);
142 break;
143 case "number":
144 cond = numCond();
145 break;
146 default:
147 return (0, codegen_1._) `typeof ${data} ${EQ} ${dataType}`;
148 }
149 return correct === DataType.Correct ? cond : (0, codegen_1.not)(cond);
150 function numCond(_cond = codegen_1.nil) {
151 return (0, codegen_1.and)((0, codegen_1._) `typeof ${data} == "number"`, _cond, strictNums ? (0, codegen_1._) `isFinite(${data})` : codegen_1.nil);
152 }
153}
154exports.checkDataType = checkDataType;
155function 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 = (0, util_1.toHash)(dataTypes);
161 if (types.array && types.object) {
162 const notObj = (0, codegen_1._) `typeof ${data} != "object"`;
163 cond = types.null ? notObj : (0, 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 = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct));
175 return cond;
176}
177exports.checkDataTypes = checkDataTypes;
178const typeError = {
179 message: ({ schema }) => `must be ${schema}`,
180 params: ({ schema, schemaValue }) => typeof schema == "string" ? (0, codegen_1._) `{type: ${schema}}` : (0, codegen_1._) `{type: ${schemaValue}}`,
181};
182function reportTypeError(it) {
183 const cxt = getTypeErrorContext(it);
184 (0, errors_1.reportError)(cxt, typeError);
185}
186exports.reportTypeError = reportTypeError;
187function getTypeErrorContext(it) {
188 const { gen, data, schema } = it;
189 const schemaCode = (0, 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
\No newline at end of file