1 | 'use strict';
|
---|
2 |
|
---|
3 | var IDENTIFIER = /^[a-z_$][a-z0-9_$-]*$/i;
|
---|
4 | var customRuleCode = require('./dotjs/custom');
|
---|
5 | var definitionSchema = require('./definition_schema');
|
---|
6 |
|
---|
7 | module.exports = {
|
---|
8 | add: addKeyword,
|
---|
9 | get: getKeyword,
|
---|
10 | remove: removeKeyword,
|
---|
11 | validate: validateKeyword
|
---|
12 | };
|
---|
13 |
|
---|
14 |
|
---|
15 | /**
|
---|
16 | * Define custom keyword
|
---|
17 | * @this Ajv
|
---|
18 | * @param {String} keyword custom keyword, should be unique (including different from all standard, custom and macro keywords).
|
---|
19 | * @param {Object} definition keyword definition object with properties `type` (type(s) which the keyword applies to), `validate` or `compile`.
|
---|
20 | * @return {Ajv} this for method chaining
|
---|
21 | */
|
---|
22 | function addKeyword(keyword, definition) {
|
---|
23 | /* jshint validthis: true */
|
---|
24 | /* eslint no-shadow: 0 */
|
---|
25 | var RULES = this.RULES;
|
---|
26 | if (RULES.keywords[keyword])
|
---|
27 | throw new Error('Keyword ' + keyword + ' is already defined');
|
---|
28 |
|
---|
29 | if (!IDENTIFIER.test(keyword))
|
---|
30 | throw new Error('Keyword ' + keyword + ' is not a valid identifier');
|
---|
31 |
|
---|
32 | if (definition) {
|
---|
33 | this.validateKeyword(definition, true);
|
---|
34 |
|
---|
35 | var dataType = definition.type;
|
---|
36 | if (Array.isArray(dataType)) {
|
---|
37 | for (var i=0; i<dataType.length; i++)
|
---|
38 | _addRule(keyword, dataType[i], definition);
|
---|
39 | } else {
|
---|
40 | _addRule(keyword, dataType, definition);
|
---|
41 | }
|
---|
42 |
|
---|
43 | var metaSchema = definition.metaSchema;
|
---|
44 | if (metaSchema) {
|
---|
45 | if (definition.$data && this._opts.$data) {
|
---|
46 | metaSchema = {
|
---|
47 | anyOf: [
|
---|
48 | metaSchema,
|
---|
49 | { '$ref': 'https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#' }
|
---|
50 | ]
|
---|
51 | };
|
---|
52 | }
|
---|
53 | definition.validateSchema = this.compile(metaSchema, true);
|
---|
54 | }
|
---|
55 | }
|
---|
56 |
|
---|
57 | RULES.keywords[keyword] = RULES.all[keyword] = true;
|
---|
58 |
|
---|
59 |
|
---|
60 | function _addRule(keyword, dataType, definition) {
|
---|
61 | var ruleGroup;
|
---|
62 | for (var i=0; i<RULES.length; i++) {
|
---|
63 | var rg = RULES[i];
|
---|
64 | if (rg.type == dataType) {
|
---|
65 | ruleGroup = rg;
|
---|
66 | break;
|
---|
67 | }
|
---|
68 | }
|
---|
69 |
|
---|
70 | if (!ruleGroup) {
|
---|
71 | ruleGroup = { type: dataType, rules: [] };
|
---|
72 | RULES.push(ruleGroup);
|
---|
73 | }
|
---|
74 |
|
---|
75 | var rule = {
|
---|
76 | keyword: keyword,
|
---|
77 | definition: definition,
|
---|
78 | custom: true,
|
---|
79 | code: customRuleCode,
|
---|
80 | implements: definition.implements
|
---|
81 | };
|
---|
82 | ruleGroup.rules.push(rule);
|
---|
83 | RULES.custom[keyword] = rule;
|
---|
84 | }
|
---|
85 |
|
---|
86 | return this;
|
---|
87 | }
|
---|
88 |
|
---|
89 |
|
---|
90 | /**
|
---|
91 | * Get keyword
|
---|
92 | * @this Ajv
|
---|
93 | * @param {String} keyword pre-defined or custom keyword.
|
---|
94 | * @return {Object|Boolean} custom keyword definition, `true` if it is a predefined keyword, `false` otherwise.
|
---|
95 | */
|
---|
96 | function getKeyword(keyword) {
|
---|
97 | /* jshint validthis: true */
|
---|
98 | var rule = this.RULES.custom[keyword];
|
---|
99 | return rule ? rule.definition : this.RULES.keywords[keyword] || false;
|
---|
100 | }
|
---|
101 |
|
---|
102 |
|
---|
103 | /**
|
---|
104 | * Remove keyword
|
---|
105 | * @this Ajv
|
---|
106 | * @param {String} keyword pre-defined or custom keyword.
|
---|
107 | * @return {Ajv} this for method chaining
|
---|
108 | */
|
---|
109 | function removeKeyword(keyword) {
|
---|
110 | /* jshint validthis: true */
|
---|
111 | var RULES = this.RULES;
|
---|
112 | delete RULES.keywords[keyword];
|
---|
113 | delete RULES.all[keyword];
|
---|
114 | delete RULES.custom[keyword];
|
---|
115 | for (var i=0; i<RULES.length; i++) {
|
---|
116 | var rules = RULES[i].rules;
|
---|
117 | for (var j=0; j<rules.length; j++) {
|
---|
118 | if (rules[j].keyword == keyword) {
|
---|
119 | rules.splice(j, 1);
|
---|
120 | break;
|
---|
121 | }
|
---|
122 | }
|
---|
123 | }
|
---|
124 | return this;
|
---|
125 | }
|
---|
126 |
|
---|
127 |
|
---|
128 | /**
|
---|
129 | * Validate keyword definition
|
---|
130 | * @this Ajv
|
---|
131 | * @param {Object} definition keyword definition object.
|
---|
132 | * @param {Boolean} throwError true to throw exception if definition is invalid
|
---|
133 | * @return {boolean} validation result
|
---|
134 | */
|
---|
135 | function validateKeyword(definition, throwError) {
|
---|
136 | validateKeyword.errors = null;
|
---|
137 | var v = this._validateKeyword = this._validateKeyword
|
---|
138 | || this.compile(definitionSchema, true);
|
---|
139 |
|
---|
140 | if (v(definition)) return true;
|
---|
141 | validateKeyword.errors = v.errors;
|
---|
142 | if (throwError)
|
---|
143 | throw new Error('custom keyword definition is invalid: ' + this.errorsText(v.errors));
|
---|
144 | else
|
---|
145 | return false;
|
---|
146 | }
|
---|