| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 | 'use strict';var resolve = require('./resolve')  , util = require('./util')  , errorClasses = require('./error_classes')  , stableStringify = require('fast-json-stable-stringify');var validateGenerator = require('../dotjs/validate');/** * Functions below are used inside compiled validations function */var ucs2length = util.ucs2length;var equal = require('fast-deep-equal');// this error is thrown by async schemas to return validation errors via exceptionvar ValidationError = errorClasses.Validation;module.exports = compile;/** * Compiles schema to validation function * @this   Ajv * @param  {Object} schema schema object * @param  {Object} root object with information about the root schema for this schema * @param  {Object} localRefs the hash of local references inside the schema (created by resolve.id), used for inline resolution * @param  {String} baseId base ID for IDs in the schema * @return {Function} validation function */function compile(schema, root, localRefs, baseId) {  /* jshint validthis: true, evil: true */  /* eslint no-shadow: 0 */  var self = this    , opts = this._opts    , refVal = [ undefined ]    , refs = {}    , patterns = []    , patternsHash = {}    , defaults = []    , defaultsHash = {}    , customRules = [];  root = root || { schema: schema, refVal: refVal, refs: refs };  var c = checkCompiling.call(this, schema, root, baseId);  var compilation = this._compilations[c.index];  if (c.compiling) return (compilation.callValidate = callValidate);  var formats = this._formats;  var RULES = this.RULES;  try {    var v = localCompile(schema, root, localRefs, baseId);    compilation.validate = v;    var cv = compilation.callValidate;    if (cv) {      cv.schema = v.schema;      cv.errors = null;      cv.refs = v.refs;      cv.refVal = v.refVal;      cv.root = v.root;      cv.$async = v.$async;      if (opts.sourceCode) cv.source = v.source;    }    return v;  } finally {    endCompiling.call(this, schema, root, baseId);  }  /* @this   {*} - custom context, see passContext option */  function callValidate() {    /* jshint validthis: true */    var validate = compilation.validate;    var result = validate.apply(this, arguments);    callValidate.errors = validate.errors;    return result;  }  function localCompile(_schema, _root, localRefs, baseId) {    var isRoot = !_root || (_root && _root.schema == _schema);    if (_root.schema != root.schema)      return compile.call(self, _schema, _root, localRefs, baseId);    var $async = _schema.$async === true;    var sourceCode = validateGenerator({      isTop: true,      schema: _schema,      isRoot: isRoot,      baseId: baseId,      root: _root,      schemaPath: '',      errSchemaPath: '#',      errorPath: '""',      MissingRefError: errorClasses.MissingRef,      RULES: RULES,      validate: validateGenerator,      util: util,      resolve: resolve,      resolveRef: resolveRef,      usePattern: usePattern,      useDefault: useDefault,      useCustomRule: useCustomRule,      opts: opts,      formats: formats,      logger: self.logger,      self: self    });    sourceCode = vars(refVal, refValCode) + vars(patterns, patternCode)                   + vars(defaults, defaultCode) + vars(customRules, customRuleCode)                   + sourceCode;    if (opts.processCode) sourceCode = opts.processCode(sourceCode, _schema);    // console.log('\n\n\n *** \n', JSON.stringify(sourceCode));    var validate;    try {      var makeValidate = new Function(        'self',        'RULES',        'formats',        'root',        'refVal',        'defaults',        'customRules',        'equal',        'ucs2length',        'ValidationError',        sourceCode      );      validate = makeValidate(        self,        RULES,        formats,        root,        refVal,        defaults,        customRules,        equal,        ucs2length,        ValidationError      );      refVal[0] = validate;    } catch(e) {      self.logger.error('Error compiling schema, function code:', sourceCode);      throw e;    }    validate.schema = _schema;    validate.errors = null;    validate.refs = refs;    validate.refVal = refVal;    validate.root = isRoot ? validate : _root;    if ($async) validate.$async = true;    if (opts.sourceCode === true) {      validate.source = {        code: sourceCode,        patterns: patterns,        defaults: defaults      };    }    return validate;  }  function resolveRef(baseId, ref, isRoot) {    ref = resolve.url(baseId, ref);    var refIndex = refs[ref];    var _refVal, refCode;    if (refIndex !== undefined) {      _refVal = refVal[refIndex];      refCode = 'refVal[' + refIndex + ']';      return resolvedRef(_refVal, refCode);    }    if (!isRoot && root.refs) {      var rootRefId = root.refs[ref];      if (rootRefId !== undefined) {        _refVal = root.refVal[rootRefId];        refCode = addLocalRef(ref, _refVal);        return resolvedRef(_refVal, refCode);      }    }    refCode = addLocalRef(ref);    var v = resolve.call(self, localCompile, root, ref);    if (v === undefined) {      var localSchema = localRefs && localRefs[ref];      if (localSchema) {        v = resolve.inlineRef(localSchema, opts.inlineRefs)            ? localSchema            : compile.call(self, localSchema, root, localRefs, baseId);      }    }    if (v === undefined) {      removeLocalRef(ref);    } else {      replaceLocalRef(ref, v);      return resolvedRef(v, refCode);    }  }  function addLocalRef(ref, v) {    var refId = refVal.length;    refVal[refId] = v;    refs[ref] = refId;    return 'refVal' + refId;  }  function removeLocalRef(ref) {    delete refs[ref];  }  function replaceLocalRef(ref, v) {    var refId = refs[ref];    refVal[refId] = v;  }  function resolvedRef(refVal, code) {    return typeof refVal == 'object' || typeof refVal == 'boolean'            ? { code: code, schema: refVal, inline: true }            : { code: code, $async: refVal && !!refVal.$async };  }  function usePattern(regexStr) {    var index = patternsHash[regexStr];    if (index === undefined) {      index = patternsHash[regexStr] = patterns.length;      patterns[index] = regexStr;    }    return 'pattern' + index;  }  function useDefault(value) {    switch (typeof value) {      case 'boolean':      case 'number':        return '' + value;      case 'string':        return util.toQuotedString(value);      case 'object':        if (value === null) return 'null';        var valueStr = stableStringify(value);        var index = defaultsHash[valueStr];        if (index === undefined) {          index = defaultsHash[valueStr] = defaults.length;          defaults[index] = value;        }        return 'default' + index;    }  }  function useCustomRule(rule, schema, parentSchema, it) {    if (self._opts.validateSchema !== false) {      var deps = rule.definition.dependencies;      if (deps && !deps.every(function(keyword) {        return Object.prototype.hasOwnProperty.call(parentSchema, keyword);      }))        throw new Error('parent schema must have all required keywords: ' + deps.join(','));      var validateSchema = rule.definition.validateSchema;      if (validateSchema) {        var valid = validateSchema(schema);        if (!valid) {          var message = 'keyword schema is invalid: ' + self.errorsText(validateSchema.errors);          if (self._opts.validateSchema == 'log') self.logger.error(message);          else throw new Error(message);        }      }    }    var compile = rule.definition.compile      , inline = rule.definition.inline      , macro = rule.definition.macro;    var validate;    if (compile) {      validate = compile.call(self, schema, parentSchema, it);    } else if (macro) {      validate = macro.call(self, schema, parentSchema, it);      if (opts.validateSchema !== false) self.validateSchema(validate, true);    } else if (inline) {      validate = inline.call(self, it, rule.keyword, schema, parentSchema);    } else {      validate = rule.definition.validate;      if (!validate) return;    }    if (validate === undefined)      throw new Error('custom keyword "' + rule.keyword + '"failed to compile');    var index = customRules.length;    customRules[index] = validate;    return {      code: 'customRule' + index,      validate: validate    };  }}/** * Checks if the schema is currently compiled * @this   Ajv * @param  {Object} schema schema to compile * @param  {Object} root root object * @param  {String} baseId base schema ID * @return {Object} object with properties "index" (compilation index) and "compiling" (boolean) */function checkCompiling(schema, root, baseId) {  /* jshint validthis: true */  var index = compIndex.call(this, schema, root, baseId);  if (index >= 0) return { index: index, compiling: true };  index = this._compilations.length;  this._compilations[index] = {    schema: schema,    root: root,    baseId: baseId  };  return { index: index, compiling: false };}/** * Removes the schema from the currently compiled list * @this   Ajv * @param  {Object} schema schema to compile * @param  {Object} root root object * @param  {String} baseId base schema ID */function endCompiling(schema, root, baseId) {  /* jshint validthis: true */  var i = compIndex.call(this, schema, root, baseId);  if (i >= 0) this._compilations.splice(i, 1);}/** * Index of schema compilation in the currently compiled list * @this   Ajv * @param  {Object} schema schema to compile * @param  {Object} root root object * @param  {String} baseId base schema ID * @return {Integer} compilation index */function compIndex(schema, root, baseId) {  /* jshint validthis: true */  for (var i=0; i<this._compilations.length; i++) {    var c = this._compilations[i];    if (c.schema == schema && c.root == root && c.baseId == baseId) return i;  }  return -1;}function patternCode(i, patterns) {  return 'var pattern' + i + ' = new RegExp(' + util.toQuotedString(patterns[i]) + ');';}function defaultCode(i) {  return 'var default' + i + ' = defaults[' + i + '];';}function refValCode(i, refVal) {  return refVal[i] === undefined ? '' : 'var refVal' + i + ' = refVal[' + i + '];';}function customRuleCode(i) {  return 'var customRule' + i + ' = customRules[' + i + '];';}function vars(arr, statement) {  if (!arr.length) return '';  var code = '';  for (var i=0; i<arr.length; i++)    code += statement(i, arr);  return code;}
 |