Path: blob/master/node_modules/ajv/lib/compile/index.js
1126 views
'use strict';12var resolve = require('./resolve')3, util = require('./util')4, errorClasses = require('./error_classes')5, stableStringify = require('fast-json-stable-stringify');67var validateGenerator = require('../dotjs/validate');89/**10* Functions below are used inside compiled validations function11*/1213var ucs2length = util.ucs2length;14var equal = require('fast-deep-equal');1516// this error is thrown by async schemas to return validation errors via exception17var ValidationError = errorClasses.Validation;1819module.exports = compile;202122/**23* Compiles schema to validation function24* @this Ajv25* @param {Object} schema schema object26* @param {Object} root object with information about the root schema for this schema27* @param {Object} localRefs the hash of local references inside the schema (created by resolve.id), used for inline resolution28* @param {String} baseId base ID for IDs in the schema29* @return {Function} validation function30*/31function compile(schema, root, localRefs, baseId) {32/* jshint validthis: true, evil: true */33/* eslint no-shadow: 0 */34var self = this35, opts = this._opts36, refVal = [ undefined ]37, refs = {}38, patterns = []39, patternsHash = {}40, defaults = []41, defaultsHash = {}42, customRules = [];4344root = root || { schema: schema, refVal: refVal, refs: refs };4546var c = checkCompiling.call(this, schema, root, baseId);47var compilation = this._compilations[c.index];48if (c.compiling) return (compilation.callValidate = callValidate);4950var formats = this._formats;51var RULES = this.RULES;5253try {54var v = localCompile(schema, root, localRefs, baseId);55compilation.validate = v;56var cv = compilation.callValidate;57if (cv) {58cv.schema = v.schema;59cv.errors = null;60cv.refs = v.refs;61cv.refVal = v.refVal;62cv.root = v.root;63cv.$async = v.$async;64if (opts.sourceCode) cv.source = v.source;65}66return v;67} finally {68endCompiling.call(this, schema, root, baseId);69}7071/* @this {*} - custom context, see passContext option */72function callValidate() {73/* jshint validthis: true */74var validate = compilation.validate;75var result = validate.apply(this, arguments);76callValidate.errors = validate.errors;77return result;78}7980function localCompile(_schema, _root, localRefs, baseId) {81var isRoot = !_root || (_root && _root.schema == _schema);82if (_root.schema != root.schema)83return compile.call(self, _schema, _root, localRefs, baseId);8485var $async = _schema.$async === true;8687var sourceCode = validateGenerator({88isTop: true,89schema: _schema,90isRoot: isRoot,91baseId: baseId,92root: _root,93schemaPath: '',94errSchemaPath: '#',95errorPath: '""',96MissingRefError: errorClasses.MissingRef,97RULES: RULES,98validate: validateGenerator,99util: util,100resolve: resolve,101resolveRef: resolveRef,102usePattern: usePattern,103useDefault: useDefault,104useCustomRule: useCustomRule,105opts: opts,106formats: formats,107logger: self.logger,108self: self109});110111sourceCode = vars(refVal, refValCode) + vars(patterns, patternCode)112+ vars(defaults, defaultCode) + vars(customRules, customRuleCode)113+ sourceCode;114115if (opts.processCode) sourceCode = opts.processCode(sourceCode, _schema);116// console.log('\n\n\n *** \n', JSON.stringify(sourceCode));117var validate;118try {119var makeValidate = new Function(120'self',121'RULES',122'formats',123'root',124'refVal',125'defaults',126'customRules',127'equal',128'ucs2length',129'ValidationError',130sourceCode131);132133validate = makeValidate(134self,135RULES,136formats,137root,138refVal,139defaults,140customRules,141equal,142ucs2length,143ValidationError144);145146refVal[0] = validate;147} catch(e) {148self.logger.error('Error compiling schema, function code:', sourceCode);149throw e;150}151152validate.schema = _schema;153validate.errors = null;154validate.refs = refs;155validate.refVal = refVal;156validate.root = isRoot ? validate : _root;157if ($async) validate.$async = true;158if (opts.sourceCode === true) {159validate.source = {160code: sourceCode,161patterns: patterns,162defaults: defaults163};164}165166return validate;167}168169function resolveRef(baseId, ref, isRoot) {170ref = resolve.url(baseId, ref);171var refIndex = refs[ref];172var _refVal, refCode;173if (refIndex !== undefined) {174_refVal = refVal[refIndex];175refCode = 'refVal[' + refIndex + ']';176return resolvedRef(_refVal, refCode);177}178if (!isRoot && root.refs) {179var rootRefId = root.refs[ref];180if (rootRefId !== undefined) {181_refVal = root.refVal[rootRefId];182refCode = addLocalRef(ref, _refVal);183return resolvedRef(_refVal, refCode);184}185}186187refCode = addLocalRef(ref);188var v = resolve.call(self, localCompile, root, ref);189if (v === undefined) {190var localSchema = localRefs && localRefs[ref];191if (localSchema) {192v = resolve.inlineRef(localSchema, opts.inlineRefs)193? localSchema194: compile.call(self, localSchema, root, localRefs, baseId);195}196}197198if (v === undefined) {199removeLocalRef(ref);200} else {201replaceLocalRef(ref, v);202return resolvedRef(v, refCode);203}204}205206function addLocalRef(ref, v) {207var refId = refVal.length;208refVal[refId] = v;209refs[ref] = refId;210return 'refVal' + refId;211}212213function removeLocalRef(ref) {214delete refs[ref];215}216217function replaceLocalRef(ref, v) {218var refId = refs[ref];219refVal[refId] = v;220}221222function resolvedRef(refVal, code) {223return typeof refVal == 'object' || typeof refVal == 'boolean'224? { code: code, schema: refVal, inline: true }225: { code: code, $async: refVal && !!refVal.$async };226}227228function usePattern(regexStr) {229var index = patternsHash[regexStr];230if (index === undefined) {231index = patternsHash[regexStr] = patterns.length;232patterns[index] = regexStr;233}234return 'pattern' + index;235}236237function useDefault(value) {238switch (typeof value) {239case 'boolean':240case 'number':241return '' + value;242case 'string':243return util.toQuotedString(value);244case 'object':245if (value === null) return 'null';246var valueStr = stableStringify(value);247var index = defaultsHash[valueStr];248if (index === undefined) {249index = defaultsHash[valueStr] = defaults.length;250defaults[index] = value;251}252return 'default' + index;253}254}255256function useCustomRule(rule, schema, parentSchema, it) {257if (self._opts.validateSchema !== false) {258var deps = rule.definition.dependencies;259if (deps && !deps.every(function(keyword) {260return Object.prototype.hasOwnProperty.call(parentSchema, keyword);261}))262throw new Error('parent schema must have all required keywords: ' + deps.join(','));263264var validateSchema = rule.definition.validateSchema;265if (validateSchema) {266var valid = validateSchema(schema);267if (!valid) {268var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);269if (self._opts.validateSchema == 'log') self.logger.error(message);270else throw new Error(message);271}272}273}274275var compile = rule.definition.compile276, inline = rule.definition.inline277, macro = rule.definition.macro;278279var validate;280if (compile) {281validate = compile.call(self, schema, parentSchema, it);282} else if (macro) {283validate = macro.call(self, schema, parentSchema, it);284if (opts.validateSchema !== false) self.validateSchema(validate, true);285} else if (inline) {286validate = inline.call(self, it, rule.keyword, schema, parentSchema);287} else {288validate = rule.definition.validate;289if (!validate) return;290}291292if (validate === undefined)293throw new Error('custom keyword "' + rule.keyword + '"failed to compile');294295var index = customRules.length;296customRules[index] = validate;297298return {299code: 'customRule' + index,300validate: validate301};302}303}304305306/**307* Checks if the schema is currently compiled308* @this Ajv309* @param {Object} schema schema to compile310* @param {Object} root root object311* @param {String} baseId base schema ID312* @return {Object} object with properties "index" (compilation index) and "compiling" (boolean)313*/314function checkCompiling(schema, root, baseId) {315/* jshint validthis: true */316var index = compIndex.call(this, schema, root, baseId);317if (index >= 0) return { index: index, compiling: true };318index = this._compilations.length;319this._compilations[index] = {320schema: schema,321root: root,322baseId: baseId323};324return { index: index, compiling: false };325}326327328/**329* Removes the schema from the currently compiled list330* @this Ajv331* @param {Object} schema schema to compile332* @param {Object} root root object333* @param {String} baseId base schema ID334*/335function endCompiling(schema, root, baseId) {336/* jshint validthis: true */337var i = compIndex.call(this, schema, root, baseId);338if (i >= 0) this._compilations.splice(i, 1);339}340341342/**343* Index of schema compilation in the currently compiled list344* @this Ajv345* @param {Object} schema schema to compile346* @param {Object} root root object347* @param {String} baseId base schema ID348* @return {Integer} compilation index349*/350function compIndex(schema, root, baseId) {351/* jshint validthis: true */352for (var i=0; i<this._compilations.length; i++) {353var c = this._compilations[i];354if (c.schema == schema && c.root == root && c.baseId == baseId) return i;355}356return -1;357}358359360function patternCode(i, patterns) {361return 'var pattern' + i + ' = new RegExp(' + util.toQuotedString(patterns[i]) + ');';362}363364365function defaultCode(i) {366return 'var default' + i + ' = defaults[' + i + '];';367}368369370function refValCode(i, refVal) {371return refVal[i] === undefined ? '' : 'var refVal' + i + ' = refVal[' + i + '];';372}373374375function customRuleCode(i) {376return 'var customRule' + i + ' = customRules[' + i + '];';377}378379380function vars(arr, statement) {381if (!arr.length) return '';382var code = '';383for (var i=0; i<arr.length; i++)384code += statement(i, arr);385return code;386}387388389