diff --git a/lib/compile/index.js b/lib/compile/index.js index 72e6daa040..83f1fb0079 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -15,6 +15,7 @@ import { import {getSchemaTypes, coerceAndCheckDataType} from "./validate/dataType" import {booleanOrEmptySchema} from "./validate/boolSchema" import {assignDefaults} from "./validate/defaults" +import schemaKeywords from "./validate/keywords" const equal = require("fast-deep-equal") const ucs2length = require("./ucs2length") @@ -128,6 +129,7 @@ function compile(schema, root, localRefs, baseId) { booleanOrEmptySchema, // TODO remove when validate is replaced commentKeyword, // TODO remove when validate is replaced assignDefaults, // TODO remove when validate is replaced + schemaKeywords, // TODO remove when validate is replaced util: util, resolve: resolve, resolveRef: resolveRef, diff --git a/lib/compile/validate/applicability.ts b/lib/compile/validate/applicability.ts index 31f32628d8..a42c95627b 100644 --- a/lib/compile/validate/applicability.ts +++ b/lib/compile/validate/applicability.ts @@ -9,7 +9,7 @@ export function shouldUseGroup(schema, group): boolean { return group.rules.some((rule) => shouldUseRule(schema, rule)) } -function shouldUseRule(schema, rule): boolean { +export function shouldUseRule(schema, rule): boolean { return schema[rule.keyword] !== undefined || ruleImplementsSomeKeyword(schema, rule) } diff --git a/lib/compile/validate/dataType.ts b/lib/compile/validate/dataType.ts index f938fbf49b..6eb1228b6d 100644 --- a/lib/compile/validate/dataType.ts +++ b/lib/compile/validate/dataType.ts @@ -24,23 +24,25 @@ export function getSchemaTypes({schema, opts}: CompilationContext): string[] { } } -export function coerceAndCheckDataType(it: CompilationContext, types: string[]): void { +export function coerceAndCheckDataType(it: CompilationContext, types: string[]): boolean { const { gen, dataLevel, opts: {coerceTypes, strictNumbers}, } = it let coerceTo = coerceToTypes(types, coerceTypes) - if ( - types.length && - (coerceTo.length || types.length > 1 || !schemaHasRulesForType(it, types[0])) - ) { + const checkTypes = + types.length > 0 && + (coerceTo.length > 0 || types.length > 1 || !schemaHasRulesForType(it, types[0])) + if (checkTypes) { + // TODO refactor `data${dataLevel || ""}` const wrongType = checkDataTypes(types, `data${dataLevel || ""}`, strictNumbers, true) gen.code(`if (${wrongType}) {`) if (coerceTo.length) coerceData(it, coerceTo) else reportTypeError(it) gen.code("}") } + return checkTypes } const COERCIBLE = toHash(["string", "number", "integer", "boolean", "null"]) @@ -136,7 +138,7 @@ const typeError: KeywordErrorDefinition = { // TODO maybe combine with boolSchemaError // TODO refactor type keyword context creation -function reportTypeError(it: CompilationContext) { +export function reportTypeError(it: CompilationContext) { const {gen, schema, schemaPath, dataLevel} = it const schemaCode = schemaRefOrVal(schema, schemaPath, "type") const cxt: KeywordContext = { diff --git a/lib/compile/validate/keywords.ts b/lib/compile/validate/keywords.ts new file mode 100644 index 0000000000..399bf067d6 --- /dev/null +++ b/lib/compile/validate/keywords.ts @@ -0,0 +1,129 @@ +import {CompilationContext} from "../../types" +import {shouldUseGroup, shouldUseRule} from "./applicability" +import {checkDataType, schemaHasRulesExcept} from "../util" +import {assignDefaults} from "./defaults" +import {reportTypeError} from "./dataType" + +export default function schemaKeywords( + it: CompilationContext, + types: string[], + typeErrors: boolean, + top: boolean +) { + const { + gen, + schema, + level, + dataLevel, + RULES, + opts: {allErrors, strictNumbers, useDefaults}, + } = it + let closingBraces2 = "" + if (schema.$ref && !schemaHasRulesExcept(schema, RULES.all, "$ref")) { + gen.code(RULES.all.$ref.code(it, "$ref")) + if (!allErrors) { + // TODO refactor with below + const errCount = top ? "0" : `errs_${level}` + gen.code(`if (errors === ${errCount}) {`) + closingBraces2 += "}" + } + } else { + for (const group of RULES) { + if (shouldUseGroup(schema, group)) { + if (group.type) { + // TODO refactor `data${dataLevel || ""}` + const checkType = checkDataType(group.type, `data${dataLevel || ""}`, strictNumbers) + gen.code(`if (${checkType}) {`) + } + if (useDefaults) assignDefaults(it, group) + let closingBraces1 = "" + for (const rule of group.rules) { + if (shouldUseRule(schema, rule)) { + const code = rule.code(it, rule.keyword, group.type) + if (code) { + gen.code(code) + if (!allErrors) closingBraces1 += "}" + } + } + } + if (!allErrors) gen.code(closingBraces1) + if (group.type) { + gen.code("}") + if (types.length === 1 && types[0] === group.type && typeErrors) { + gen.code(`else {`) + reportTypeError(it) + gen.code(`}`) + } + } + if (!allErrors) { + const errCount = top ? "0" : `errs_${level}` + gen.code(`if (errors === ${errCount}) {`) + closingBraces2 += "}" + } + } + } + } + if (!allErrors) gen.code(closingBraces2) +} + +// {{? it.schema.$ref && !$refKeywords }} +// {{= it.RULES.all.$ref.code(it, '$ref') }} +// {{? $breakOnError }} +// } +// if (errors === {{?$top}}0{{??}}errs_{{=$lvl}}{{?}}) { +// {{ $closingBraces2 += '}'; }} +// {{?}} +// {{??}} +// {{~ it.RULES:$rulesGroup }} +// {{? $shouldUseGroup($rulesGroup) }} +// {{? $rulesGroup.type }} +// if ({{= it.util.checkDataType($rulesGroup.type, $data, it.opts.strictNumbers) }}) { +// {{?}} +// {{? it.opts.useDefaults }} +// {{ +// it.gen._out = ""; +// it.assignDefaults(it, $rulesGroup); +// }} +// {{=it.gen._out}} +// {{?}} +// + +// {{~ $rulesGroup.rules:$rule }} +// {{? $shouldUseRule($rule) }} +// {{ var $code = $rule.code(it, $rule.keyword, $rulesGroup.type); }} +// {{? $code }} +// {{= $code }} +// {{? $breakOnError }} +// {{ $closingBraces1 += '}'; }} +// {{?}} +// {{?}} +// {{?}} +// {{~}} + +// {{? $breakOnError }} +// {{= $closingBraces1 }} +// {{ $closingBraces1 = ''; }} +// {{?}} + +// {{? $rulesGroup.type }} +// } +// {{? $typeSchema && $typeSchema === $rulesGroup.type && !(it.opts.coerceTypes && it.util.coerceToTypes(it.opts.coerceTypes, $typeSchema)) }} +// else { +// {{ +// var $schemaPath = it.schemaPath + '.type' +// , $errSchemaPath = it.errSchemaPath + '/type'; +// }} +// {{# def.error:'type' }} +// } +// {{?}} +// {{?}} + +// {{? $breakOnError }} +// if (errors === {{?$top}}0{{??}}errs_{{=$lvl}}{{?}}) { +// {{ $closingBraces2 += '}'; }} +// {{?}} +// {{?}} +// {{~}} +// {{?}} + +// {{? $breakOnError }} {{= $closingBraces2 }} {{?}} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 2af57098b3..9f78ea1c31 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -118,7 +118,7 @@ const types = it.getSchemaTypes(it); /* TODO do not clear _out */ it.gen._out = ""; - it.coerceAndCheckDataType(it, types); + const checkedTypes = it.coerceAndCheckDataType(it, types); }} {{= it.gen._out }} @@ -159,7 +159,7 @@ {{?}} {{? $rulesGroup.type }} } - {{? $typeSchema && $typeSchema === $rulesGroup.type && !(it.opts.coerceTypes && it.util.coerceToTypes(it.opts.coerceTypes, $typeSchema)) }} + {{? $typeSchema && $typeSchema === $rulesGroup.type && !checkedTypes }} else { {{ var $schemaPath = it.schemaPath + '.type'