Spaces:
Running
Running
; | |
var IDENTIFIER = /^[a-z_$][a-z0-9_$-]*$/i; | |
var customRuleCode = require('./dotjs/custom'); | |
var definitionSchema = require('./definition_schema'); | |
module.exports = { | |
add: addKeyword, | |
get: getKeyword, | |
remove: removeKeyword, | |
validate: validateKeyword | |
}; | |
/** | |
* Define custom keyword | |
* @this Ajv | |
* @param {String} keyword custom keyword, should be unique (including different from all standard, custom and macro keywords). | |
* @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`. | |
* @return {Ajv} this for method chaining | |
*/ | |
function addKeyword(keyword, definition) { | |
/* jshint validthis: true */ | |
/* eslint no-shadow: 0 */ | |
var RULES = this.RULES; | |
if (RULES.keywords[keyword]) | |
throw new Error('Keyword ' + keyword + ' is already defined'); | |
if (!IDENTIFIER.test(keyword)) | |
throw new Error('Keyword ' + keyword + ' is not a valid identifier'); | |
if (definition) { | |
this.validateKeyword(definition, true); | |
var dataType = definition.type; | |
if (Array.isArray(dataType)) { | |
for (var i=0; i<dataType.length; i++) | |
_addRule(keyword, dataType[i], definition); | |
} else { | |
_addRule(keyword, dataType, definition); | |
} | |
var metaSchema = definition.metaSchema; | |
if (metaSchema) { | |
if (definition.$data && this._opts.$data) { | |
metaSchema = { | |
anyOf: [ | |
metaSchema, | |
{ '$ref': 'https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#' } | |
] | |
}; | |
} | |
definition.validateSchema = this.compile(metaSchema, true); | |
} | |
} | |
RULES.keywords[keyword] = RULES.all[keyword] = true; | |
function _addRule(keyword, dataType, definition) { | |
var ruleGroup; | |
for (var i=0; i<RULES.length; i++) { | |
var rg = RULES[i]; | |
if (rg.type == dataType) { | |
ruleGroup = rg; | |
break; | |
} | |
} | |
if (!ruleGroup) { | |
ruleGroup = { type: dataType, rules: [] }; | |
RULES.push(ruleGroup); | |
} | |
var rule = { | |
keyword: keyword, | |
definition: definition, | |
custom: true, | |
code: customRuleCode, | |
implements: definition.implements | |
}; | |
ruleGroup.rules.push(rule); | |
RULES.custom[keyword] = rule; | |
} | |
return this; | |
} | |
/** | |
* Get keyword | |
* @this Ajv | |
* @param {String} keyword pre-defined or custom keyword. | |
* @return {Object|Boolean} custom keyword definition, `true` if it is a predefined keyword, `false` otherwise. | |
*/ | |
function getKeyword(keyword) { | |
/* jshint validthis: true */ | |
var rule = this.RULES.custom[keyword]; | |
return rule ? rule.definition : this.RULES.keywords[keyword] || false; | |
} | |
/** | |
* Remove keyword | |
* @this Ajv | |
* @param {String} keyword pre-defined or custom keyword. | |
* @return {Ajv} this for method chaining | |
*/ | |
function removeKeyword(keyword) { | |
/* jshint validthis: true */ | |
var RULES = this.RULES; | |
delete RULES.keywords[keyword]; | |
delete RULES.all[keyword]; | |
delete RULES.custom[keyword]; | |
for (var i=0; i<RULES.length; i++) { | |
var rules = RULES[i].rules; | |
for (var j=0; j<rules.length; j++) { | |
if (rules[j].keyword == keyword) { | |
rules.splice(j, 1); | |
break; | |
} | |
} | |
} | |
return this; | |
} | |
/** | |
* Validate keyword definition | |
* @this Ajv | |
* @param {Object} definition keyword definition object. | |
* @param {Boolean} throwError true to throw exception if definition is invalid | |
* @return {boolean} validation result | |
*/ | |
function validateKeyword(definition, throwError) { | |
validateKeyword.errors = null; | |
var v = this._validateKeyword = this._validateKeyword | |
|| this.compile(definitionSchema, true); | |
if (v(definition)) return true; | |
validateKeyword.errors = v.errors; | |
if (throwError) | |
throw new Error('custom keyword definition is invalid: ' + this.errorsText(v.errors)); | |
else | |
return false; | |
} | |