'use strict'; module.exports = function generate_validate(it, $keyword, $ruleType) { var out = ''; var $async = it.schema.$async === true, $refKeywords = it.util.schemaHasRulesExcept( it.schema, it.RULES.all, '$ref' ), $id = it.self._getId(it.schema); if (it.opts.strictKeywords) { var $unknownKwd = it.util.schemaUnknownRules(it.schema, it.RULES.keywords); if ($unknownKwd) { var $keywordsMsg = 'unknown keyword: ' + $unknownKwd; if (it.opts.strictKeywords === 'log') it.logger.warn($keywordsMsg); else throw new Error($keywordsMsg); } } if (it.isTop) { out += ' var validate = '; if ($async) { it.async = true; out += 'async '; } out += "function(data, dataPath, parentData, parentDataProperty, rootData) { 'use strict'; "; if ($id && (it.opts.sourceCode || it.opts.processCode)) { out += ' ' + ('/\*# sourceURL=' + $id + ' */') + ' '; } } if (typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref)) { var $keyword = 'false schema'; var $lvl = it.level; var $dataLvl = it.dataLevel; var $schema = it.schema[$keyword]; var $schemaPath = it.schemaPath + it.util.getProperty($keyword); var $errSchemaPath = it.errSchemaPath + '/' + $keyword; var $breakOnError = !it.opts.allErrors; var $errorKeyword; var $data = 'data' + ($dataLvl || ''); var $valid = 'valid' + $lvl; if (it.schema === false) { if (it.isTop) { $breakOnError = true; } else { out += ' var ' + $valid + ' = false; '; } var $$outStack = $$outStack || []; $$outStack.push(out); out = ''; /* istanbul ignore else */ if (it.createErrors !== false) { out += " { keyword: '" + ($errorKeyword || 'false schema') + "' , dataPath: (dataPath || '') + " + it.errorPath + ' , schemaPath: ' + it.util.toQuotedString($errSchemaPath) + ' , params: {} '; if (it.opts.messages !== false) { out += " , message: 'boolean schema is false' "; } if (it.opts.verbose) { out += ' , schema: false , parentSchema: validate.schema' + it.schemaPath + ' , data: ' + $data + ' '; } out += ' } '; } else { out += ' {} '; } var __err = out; out = $$outStack.pop(); if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ if (it.async) { out += ' throw new ValidationError([' + __err + ']); '; } else { out += ' validate.errors = [' + __err + ']; return false; '; } } else { out += ' var err = ' + __err + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; } } else { if (it.isTop) { if ($async) { out += ' return data; '; } else { out += ' validate.errors = null; return true; '; } } else { out += ' var ' + $valid + ' = true; '; } } if (it.isTop) { out += ' }; return validate; '; } return out; } if (it.isTop) { var $top = it.isTop, $lvl = (it.level = 0), $dataLvl = (it.dataLevel = 0), $data = 'data'; it.rootId = it.resolve.fullPath(it.self._getId(it.root.schema)); it.baseId = it.baseId || it.rootId; delete it.isTop; it.dataPathArr = ['']; if ( it.schema.default !== undefined && it.opts.useDefaults && it.opts.strictDefaults ) { var $defaultMsg = 'default is ignored in the schema root'; if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg); else throw new Error($defaultMsg); } out += ' var vErrors = null; '; out += ' var errors = 0; '; out += ' if (rootData === undefined) rootData = data; '; } else { var $lvl = it.level, $dataLvl = it.dataLevel, $data = 'data' + ($dataLvl || ''); if ($id) it.baseId = it.resolve.url(it.baseId, $id); if ($async && !it.async) throw new Error('async schema in sync schema'); out += ' var errs_' + $lvl + ' = errors;'; } var $valid = 'valid' + $lvl, $breakOnError = !it.opts.allErrors, $closingBraces1 = '', $closingBraces2 = ''; var $errorKeyword; var $typeSchema = it.schema.type, $typeIsArray = Array.isArray($typeSchema); if ($typeSchema && it.opts.nullable && it.schema.nullable === true) { if ($typeIsArray) { if ($typeSchema.indexOf('null') == -1) $typeSchema = $typeSchema.concat('null'); } else if ($typeSchema != 'null') { $typeSchema = [$typeSchema, 'null']; $typeIsArray = true; } } if ($typeIsArray && $typeSchema.length == 1) { $typeSchema = $typeSchema[0]; $typeIsArray = false; } if (it.schema.$ref && $refKeywords) { if (it.opts.extendRefs == 'fail') { throw new Error( '$ref: validation keywords used in schema at path "' + it.errSchemaPath + '" (see option extendRefs)' ); } else if (it.opts.extendRefs !== true) { $refKeywords = false; it.logger.warn( '$ref: keywords ignored in schema at path "' + it.errSchemaPath + '"' ); } } if (it.schema.$comment && it.opts.$comment) { out += ' ' + it.RULES.all.$comment.code(it, '$comment'); } if ($typeSchema) { if (it.opts.coerceTypes) { var $coerceToTypes = it.util.coerceToTypes( it.opts.coerceTypes, $typeSchema ); } var $rulesGroup = it.RULES.types[$typeSchema]; if ( $coerceToTypes || $typeIsArray || $rulesGroup === true || ($rulesGroup && !$shouldUseGroup($rulesGroup)) ) { var $schemaPath = it.schemaPath + '.type', $errSchemaPath = it.errSchemaPath + '/type'; var $schemaPath = it.schemaPath + '.type', $errSchemaPath = it.errSchemaPath + '/type', $method = $typeIsArray ? 'checkDataTypes' : 'checkDataType'; out += ' if (' + it.util[$method]($typeSchema, $data, it.opts.strictNumbers, true) + ') { '; if ($coerceToTypes) { var $dataType = 'dataType' + $lvl, $coerced = 'coerced' + $lvl; out += ' var ' + $dataType + ' = typeof ' + $data + '; var ' + $coerced + ' = undefined; '; if (it.opts.coerceTypes == 'array') { out += ' if (' + $dataType + " == 'object' && Array.isArray(" + $data + ') && ' + $data + '.length == 1) { ' + $data + ' = ' + $data + '[0]; ' + $dataType + ' = typeof ' + $data + '; if (' + it.util.checkDataType( it.schema.type, $data, it.opts.strictNumbers ) + ') ' + $coerced + ' = ' + $data + '; } '; } out += ' if (' + $coerced + ' !== undefined) ; '; var arr1 = $coerceToTypes; if (arr1) { var $type, $i = -1, l1 = arr1.length - 1; while ($i < l1) { $type = arr1[($i += 1)]; if ($type == 'string') { out += ' else if (' + $dataType + " == 'number' || " + $dataType + " == 'boolean') " + $coerced + " = '' + " + $data + '; else if (' + $data + ' === null) ' + $coerced + " = ''; "; } else if ($type == 'number' || $type == 'integer') { out += ' else if (' + $dataType + " == 'boolean' || " + $data + ' === null || (' + $dataType + " == 'string' && " + $data + ' && ' + $data + ' == +' + $data + ' '; if ($type == 'integer') { out += ' && !(' + $data + ' % 1)'; } out += ')) ' + $coerced + ' = +' + $data + '; '; } else if ($type == 'boolean') { out += ' else if (' + $data + " === 'false' || " + $data + ' === 0 || ' + $data + ' === null) ' + $coerced + ' = false; else if (' + $data + " === 'true' || " + $data + ' === 1) ' + $coerced + ' = true; '; } else if ($type == 'null') { out += ' else if (' + $data + " === '' || " + $data + ' === 0 || ' + $data + ' === false) ' + $coerced + ' = null; '; } else if (it.opts.coerceTypes == 'array' && $type == 'array') { out += ' else if (' + $dataType + " == 'string' || " + $dataType + " == 'number' || " + $dataType + " == 'boolean' || " + $data + ' == null) ' + $coerced + ' = [' + $data + ']; '; } } } out += ' else { '; var $$outStack = $$outStack || []; $$outStack.push(out); out = ''; /* istanbul ignore else */ if (it.createErrors !== false) { out += " { keyword: '" + ($errorKeyword || 'type') + "' , dataPath: (dataPath || '') + " + it.errorPath + ' , schemaPath: ' + it.util.toQuotedString($errSchemaPath) + " , params: { type: '"; if ($typeIsArray) { out += '' + $typeSchema.join(','); } else { out += '' + $typeSchema; } out += "' } "; if (it.opts.messages !== false) { out += " , message: 'should be "; if ($typeIsArray) { out += '' + $typeSchema.join(','); } else { out += '' + $typeSchema; } out += "' "; } if (it.opts.verbose) { out += ' , schema: validate.schema' + $schemaPath + ' , parentSchema: validate.schema' + it.schemaPath + ' , data: ' + $data + ' '; } out += ' } '; } else { out += ' {} '; } var __err = out; out = $$outStack.pop(); if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ if (it.async) { out += ' throw new ValidationError([' + __err + ']); '; } else { out += ' validate.errors = [' + __err + ']; return false; '; } } else { out += ' var err = ' + __err + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; } out += ' } if (' + $coerced + ' !== undefined) { '; var $parentData = $dataLvl ? 'data' + ($dataLvl - 1 || '') : 'parentData', $parentDataProperty = $dataLvl ? it.dataPathArr[$dataLvl] : 'parentDataProperty'; out += ' ' + $data + ' = ' + $coerced + '; '; if (!$dataLvl) { out += 'if (' + $parentData + ' !== undefined)'; } out += ' ' + $parentData + '[' + $parentDataProperty + '] = ' + $coerced + '; } '; } else { var $$outStack = $$outStack || []; $$outStack.push(out); out = ''; /* istanbul ignore else */ if (it.createErrors !== false) { out += " { keyword: '" + ($errorKeyword || 'type') + "' , dataPath: (dataPath || '') + " + it.errorPath + ' , schemaPath: ' + it.util.toQuotedString($errSchemaPath) + " , params: { type: '"; if ($typeIsArray) { out += '' + $typeSchema.join(','); } else { out += '' + $typeSchema; } out += "' } "; if (it.opts.messages !== false) { out += " , message: 'should be "; if ($typeIsArray) { out += '' + $typeSchema.join(','); } else { out += '' + $typeSchema; } out += "' "; } if (it.opts.verbose) { out += ' , schema: validate.schema' + $schemaPath + ' , parentSchema: validate.schema' + it.schemaPath + ' , data: ' + $data + ' '; } out += ' } '; } else { out += ' {} '; } var __err = out; out = $$outStack.pop(); if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ if (it.async) { out += ' throw new ValidationError([' + __err + ']); '; } else { out += ' validate.errors = [' + __err + ']; return false; '; } } else { out += ' var err = ' + __err + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; } } out += ' } '; } } if (it.schema.$ref && !$refKeywords) { out += ' ' + it.RULES.all.$ref.code(it, '$ref') + ' '; if ($breakOnError) { out += ' } if (errors === '; if ($top) { out += '0'; } else { out += 'errs_' + $lvl; } out += ') { '; $closingBraces2 += '}'; } } else { var arr2 = it.RULES; if (arr2) { var $rulesGroup, i2 = -1, l2 = arr2.length - 1; while (i2 < l2) { $rulesGroup = arr2[(i2 += 1)]; if ($shouldUseGroup($rulesGroup)) { if ($rulesGroup.type) { out += ' if (' + it.util.checkDataType( $rulesGroup.type, $data, it.opts.strictNumbers ) + ') { '; } if (it.opts.useDefaults) { if ($rulesGroup.type == 'object' && it.schema.properties) { var $schema = it.schema.properties, $schemaKeys = Object.keys($schema); var arr3 = $schemaKeys; if (arr3) { var $propertyKey, i3 = -1, l3 = arr3.length - 1; while (i3 < l3) { $propertyKey = arr3[(i3 += 1)]; var $sch = $schema[$propertyKey]; if ($sch.default !== undefined) { var $passData = $data + it.util.getProperty($propertyKey); if (it.compositeRule) { if (it.opts.strictDefaults) { var $defaultMsg = 'default is ignored for: ' + $passData; if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg); else throw new Error($defaultMsg); } } else { out += ' if (' + $passData + ' === undefined '; if (it.opts.useDefaults == 'empty') { out += ' || ' + $passData + ' === null || ' + $passData + " === '' "; } out += ' ) ' + $passData + ' = '; if (it.opts.useDefaults == 'shared') { out += ' ' + it.useDefault($sch.default) + ' '; } else { out += ' ' + JSON.stringify($sch.default) + ' '; } out += '; '; } } } } } else if ( $rulesGroup.type == 'array' && Array.isArray(it.schema.items) ) { var arr4 = it.schema.items; if (arr4) { var $sch, $i = -1, l4 = arr4.length - 1; while ($i < l4) { $sch = arr4[($i += 1)]; if ($sch.default !== undefined) { var $passData = $data + '[' + $i + ']'; if (it.compositeRule) { if (it.opts.strictDefaults) { var $defaultMsg = 'default is ignored for: ' + $passData; if (it.opts.strictDefaults === 'log') it.logger.warn($defaultMsg); else throw new Error($defaultMsg); } } else { out += ' if (' + $passData + ' === undefined '; if (it.opts.useDefaults == 'empty') { out += ' || ' + $passData + ' === null || ' + $passData + " === '' "; } out += ' ) ' + $passData + ' = '; if (it.opts.useDefaults == 'shared') { out += ' ' + it.useDefault($sch.default) + ' '; } else { out += ' ' + JSON.stringify($sch.default) + ' '; } out += '; '; } } } } } } var arr5 = $rulesGroup.rules; if (arr5) { var $rule, i5 = -1, l5 = arr5.length - 1; while (i5 < l5) { $rule = arr5[(i5 += 1)]; if ($shouldUseRule($rule)) { var $code = $rule.code(it, $rule.keyword, $rulesGroup.type); if ($code) { out += ' ' + $code + ' '; if ($breakOnError) { $closingBraces1 += '}'; } } } } } if ($breakOnError) { out += ' ' + $closingBraces1 + ' '; $closingBraces1 = ''; } if ($rulesGroup.type) { out += ' } '; if ( $typeSchema && $typeSchema === $rulesGroup.type && !$coerceToTypes ) { out += ' else { '; var $schemaPath = it.schemaPath + '.type', $errSchemaPath = it.errSchemaPath + '/type'; var $$outStack = $$outStack || []; $$outStack.push(out); out = ''; /* istanbul ignore else */ if (it.createErrors !== false) { out += " { keyword: '" + ($errorKeyword || 'type') + "' , dataPath: (dataPath || '') + " + it.errorPath + ' , schemaPath: ' + it.util.toQuotedString($errSchemaPath) + " , params: { type: '"; if ($typeIsArray) { out += '' + $typeSchema.join(','); } else { out += '' + $typeSchema; } out += "' } "; if (it.opts.messages !== false) { out += " , message: 'should be "; if ($typeIsArray) { out += '' + $typeSchema.join(','); } else { out += '' + $typeSchema; } out += "' "; } if (it.opts.verbose) { out += ' , schema: validate.schema' + $schemaPath + ' , parentSchema: validate.schema' + it.schemaPath + ' , data: ' + $data + ' '; } out += ' } '; } else { out += ' {} '; } var __err = out; out = $$outStack.pop(); if (!it.compositeRule && $breakOnError) { /* istanbul ignore if */ if (it.async) { out += ' throw new ValidationError([' + __err + ']); '; } else { out += ' validate.errors = [' + __err + ']; return false; '; } } else { out += ' var err = ' + __err + '; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; '; } out += ' } '; } } if ($breakOnError) { out += ' if (errors === '; if ($top) { out += '0'; } else { out += 'errs_' + $lvl; } out += ') { '; $closingBraces2 += '}'; } } } } } if ($breakOnError) { out += ' ' + $closingBraces2 + ' '; } if ($top) { if ($async) { out += ' if (errors === 0) return data; '; out += ' else throw new ValidationError(vErrors); '; } else { out += ' validate.errors = vErrors; '; out += ' return errors === 0; '; } out += ' }; return validate;'; } else { out += ' var ' + $valid + ' = errors === errs_' + $lvl + ';'; } function $shouldUseGroup($rulesGroup) { var rules = $rulesGroup.rules; for (var i = 0; i < rules.length; i++) if ($shouldUseRule(rules[i])) return true; } function $shouldUseRule($rule) { return ( it.schema[$rule.keyword] !== undefined || ($rule.implements && $ruleImplementsSomeKeyword($rule)) ); } function $ruleImplementsSomeKeyword($rule) { var impl = $rule.implements; for (var i = 0; i < impl.length; i++) if (it.schema[impl[i]] !== undefined) return true; } return out; };