index.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.checkStrictMode = exports.schemaCxtHasRules = exports.subschemaCode = exports.validateFunctionCode = void 0;
  4. const boolSchema_1 = require("./boolSchema");
  5. const dataType_1 = require("./dataType");
  6. const iterate_1 = require("./iterate");
  7. const codegen_1 = require("../codegen");
  8. const names_1 = require("../names");
  9. const resolve_1 = require("../resolve");
  10. const util_1 = require("../util");
  11. // schema compilation - generates validation function, subschemaCode (below) is used for subschemas
  12. function validateFunctionCode(it) {
  13. if (isSchemaObj(it)) {
  14. checkKeywords(it);
  15. if (schemaCxtHasRules(it)) {
  16. topSchemaObjCode(it);
  17. return;
  18. }
  19. }
  20. validateFunction(it, () => boolSchema_1.topBoolOrEmptySchema(it));
  21. }
  22. exports.validateFunctionCode = validateFunctionCode;
  23. function validateFunction({ gen, validateName, schema, schemaEnv, opts }, body) {
  24. if (opts.code.es5) {
  25. gen.func(validateName, codegen_1._ `${names_1.default.data}, ${names_1.default.valCxt}`, schemaEnv.$async, () => {
  26. gen.code(codegen_1._ `"use strict"; ${funcSourceUrl(schema, opts)}`);
  27. destructureValCxtES5(gen, opts);
  28. gen.code(body);
  29. });
  30. }
  31. else {
  32. gen.func(validateName, codegen_1._ `${names_1.default.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () => gen.code(funcSourceUrl(schema, opts)).code(body));
  33. }
  34. }
  35. function destructureValCxt(opts) {
  36. return codegen_1._ `{${names_1.default.dataPath}="", ${names_1.default.parentData}, ${names_1.default.parentDataProperty}, ${names_1.default.rootData}=${names_1.default.data}${opts.dynamicRef ? codegen_1._ `, ${names_1.default.dynamicAnchors}={}` : codegen_1.nil}}={}`;
  37. }
  38. function destructureValCxtES5(gen, opts) {
  39. gen.if(names_1.default.valCxt, () => {
  40. gen.var(names_1.default.dataPath, codegen_1._ `${names_1.default.valCxt}.${names_1.default.dataPath}`);
  41. gen.var(names_1.default.parentData, codegen_1._ `${names_1.default.valCxt}.${names_1.default.parentData}`);
  42. gen.var(names_1.default.parentDataProperty, codegen_1._ `${names_1.default.valCxt}.${names_1.default.parentDataProperty}`);
  43. gen.var(names_1.default.rootData, codegen_1._ `${names_1.default.valCxt}.${names_1.default.rootData}`);
  44. if (opts.dynamicRef)
  45. gen.var(names_1.default.dynamicAnchors, codegen_1._ `${names_1.default.valCxt}.${names_1.default.dynamicAnchors}`);
  46. }, () => {
  47. gen.var(names_1.default.dataPath, codegen_1._ `""`);
  48. gen.var(names_1.default.parentData, codegen_1._ `undefined`);
  49. gen.var(names_1.default.parentDataProperty, codegen_1._ `undefined`);
  50. gen.var(names_1.default.rootData, names_1.default.data);
  51. if (opts.dynamicRef)
  52. gen.var(names_1.default.dynamicAnchors, codegen_1._ `{}`);
  53. });
  54. }
  55. function topSchemaObjCode(it) {
  56. const { schema, opts, gen } = it;
  57. validateFunction(it, () => {
  58. if (opts.$comment && schema.$comment)
  59. commentKeyword(it);
  60. checkNoDefault(it);
  61. gen.let(names_1.default.vErrors, null);
  62. gen.let(names_1.default.errors, 0);
  63. if (opts.unevaluated)
  64. resetEvaluated(it);
  65. typeAndKeywords(it);
  66. returnResults(it);
  67. });
  68. return;
  69. }
  70. function resetEvaluated(it) {
  71. // TODO maybe some hook to execute it in the end to check whether props/items are Name, as in assignEvaluated
  72. const { gen, validateName } = it;
  73. it.evaluated = gen.const("evaluated", codegen_1._ `${validateName}.evaluated`);
  74. gen.if(codegen_1._ `${it.evaluated}.dynamicProps`, () => gen.assign(codegen_1._ `${it.evaluated}.props`, codegen_1._ `undefined`));
  75. gen.if(codegen_1._ `${it.evaluated}.dynamicItems`, () => gen.assign(codegen_1._ `${it.evaluated}.items`, codegen_1._ `undefined`));
  76. }
  77. function funcSourceUrl(schema, opts) {
  78. return typeof schema == "object" && schema.$id && (opts.code.source || opts.code.process)
  79. ? codegen_1._ `/*# sourceURL=${schema.$id} */`
  80. : codegen_1.nil;
  81. }
  82. // schema compilation - this function is used recursively to generate code for sub-schemas
  83. function subschemaCode(it, valid) {
  84. if (isSchemaObj(it)) {
  85. checkKeywords(it);
  86. if (schemaCxtHasRules(it)) {
  87. subSchemaObjCode(it, valid);
  88. return;
  89. }
  90. }
  91. boolSchema_1.boolOrEmptySchema(it, valid);
  92. }
  93. exports.subschemaCode = subschemaCode;
  94. function schemaCxtHasRules({ schema, self }) {
  95. if (typeof schema == "boolean")
  96. return !schema;
  97. for (const key in schema)
  98. if (self.RULES.all[key])
  99. return true;
  100. return false;
  101. }
  102. exports.schemaCxtHasRules = schemaCxtHasRules;
  103. function isSchemaObj(it) {
  104. return typeof it.schema != "boolean";
  105. }
  106. function subSchemaObjCode(it, valid) {
  107. const { schema, gen, opts } = it;
  108. if (opts.$comment && schema.$comment)
  109. commentKeyword(it);
  110. updateContext(it);
  111. checkAsync(it);
  112. const errsCount = gen.const("_errs", names_1.default.errors);
  113. typeAndKeywords(it, errsCount);
  114. // TODO var
  115. gen.var(valid, codegen_1._ `${errsCount} === ${names_1.default.errors}`);
  116. }
  117. function checkKeywords(it) {
  118. util_1.checkUnknownRules(it);
  119. checkRefsAndKeywords(it);
  120. }
  121. function typeAndKeywords(it, errsCount) {
  122. const types = dataType_1.getSchemaTypes(it.schema);
  123. const checkedTypes = dataType_1.coerceAndCheckDataType(it, types);
  124. iterate_1.schemaKeywords(it, types, !checkedTypes, errsCount);
  125. }
  126. function checkRefsAndKeywords(it) {
  127. const { schema, errSchemaPath, opts, self } = it;
  128. if (schema.$ref && opts.ignoreKeywordsWithRef && util_1.schemaHasRulesButRef(schema, self.RULES)) {
  129. self.logger.warn(`$ref: keywords ignored in schema at path "${errSchemaPath}"`);
  130. }
  131. }
  132. function checkNoDefault(it) {
  133. const { schema, opts } = it;
  134. if (schema.default !== undefined && opts.useDefaults && opts.strict) {
  135. checkStrictMode(it, "default is ignored in the schema root");
  136. }
  137. }
  138. function updateContext(it) {
  139. if (it.schema.$id)
  140. it.baseId = resolve_1.resolveUrl(it.baseId, it.schema.$id);
  141. }
  142. function checkAsync(it) {
  143. if (it.schema.$async && !it.schemaEnv.$async)
  144. throw new Error("async schema in sync schema");
  145. }
  146. function commentKeyword({ gen, schemaEnv, schema, errSchemaPath, opts }) {
  147. const msg = schema.$comment;
  148. if (opts.$comment === true) {
  149. gen.code(codegen_1._ `${names_1.default.self}.logger.log(${msg})`);
  150. }
  151. else if (typeof opts.$comment == "function") {
  152. const schemaPath = codegen_1.str `${errSchemaPath}/$comment`;
  153. const rootName = gen.scopeValue("root", { ref: schemaEnv.root });
  154. gen.code(codegen_1._ `${names_1.default.self}.opts.$comment(${msg}, ${schemaPath}, ${rootName}.schema)`);
  155. }
  156. }
  157. function returnResults(it) {
  158. const { gen, schemaEnv, validateName, ValidationError, opts } = it;
  159. if (schemaEnv.$async) {
  160. // TODO assign unevaluated
  161. gen.if(codegen_1._ `${names_1.default.errors} === 0`, () => gen.return(names_1.default.data), () => gen.throw(codegen_1._ `new ${ValidationError}(${names_1.default.vErrors})`));
  162. }
  163. else {
  164. gen.assign(codegen_1._ `${validateName}.errors`, names_1.default.vErrors);
  165. if (opts.unevaluated)
  166. assignEvaluated(it);
  167. gen.return(codegen_1._ `${names_1.default.errors} === 0`);
  168. }
  169. }
  170. function assignEvaluated({ gen, evaluated, props, items }) {
  171. if (props instanceof codegen_1.Name)
  172. gen.assign(codegen_1._ `${evaluated}.props`, props);
  173. if (items instanceof codegen_1.Name)
  174. gen.assign(codegen_1._ `${evaluated}.items`, items);
  175. }
  176. function checkStrictMode(it, msg, mode = it.opts.strict) {
  177. if (!mode)
  178. return;
  179. msg = `strict mode: ${msg}`;
  180. if (mode === true)
  181. throw new Error(msg);
  182. it.self.logger.warn(msg);
  183. }
  184. exports.checkStrictMode = checkStrictMode;
  185. //# sourceMappingURL=index.js.map