keyword.js 3.6 KB

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