errors.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import type {KeywordErrorCxt, KeywordErrorDefinition} from "../types"
  2. import type {SchemaCxt} from "./index"
  3. import {CodeGen, _, str, strConcat, Code, Name} from "./codegen"
  4. import {SafeExpr} from "./codegen/code"
  5. import N from "./names"
  6. export const keywordError: KeywordErrorDefinition = {
  7. message: ({keyword}) => str`should pass "${keyword}" keyword validation`,
  8. }
  9. export const keyword$DataError: KeywordErrorDefinition = {
  10. message: ({keyword, schemaType}) =>
  11. schemaType
  12. ? str`"${keyword}" keyword must be ${schemaType} ($data)`
  13. : str`"${keyword}" keyword is invalid ($data)`,
  14. }
  15. export function reportError(
  16. cxt: KeywordErrorCxt,
  17. error: KeywordErrorDefinition,
  18. overrideAllErrors?: boolean
  19. ): void {
  20. const {it} = cxt
  21. const {gen, compositeRule, allErrors} = it
  22. const errObj = errorObjectCode(cxt, error)
  23. if (overrideAllErrors ?? (compositeRule || allErrors)) {
  24. addError(gen, errObj)
  25. } else {
  26. returnErrors(it, _`[${errObj}]`)
  27. }
  28. }
  29. export function reportExtraError(cxt: KeywordErrorCxt, error: KeywordErrorDefinition): void {
  30. const {it} = cxt
  31. const {gen, compositeRule, allErrors} = it
  32. const errObj = errorObjectCode(cxt, error)
  33. addError(gen, errObj)
  34. if (!(compositeRule || allErrors)) {
  35. returnErrors(it, N.vErrors)
  36. }
  37. }
  38. export function resetErrorsCount(gen: CodeGen, errsCount: Name): void {
  39. gen.assign(N.errors, errsCount)
  40. gen.if(_`${N.vErrors} !== null`, () =>
  41. gen.if(
  42. errsCount,
  43. () => gen.assign(_`${N.vErrors}.length`, errsCount),
  44. () => gen.assign(N.vErrors, null)
  45. )
  46. )
  47. }
  48. export function extendErrors({
  49. gen,
  50. keyword,
  51. schemaValue,
  52. data,
  53. errsCount,
  54. it,
  55. }: KeywordErrorCxt): void {
  56. /* istanbul ignore if */
  57. if (errsCount === undefined) throw new Error("ajv implementation error")
  58. const err = gen.name("err")
  59. gen.forRange("i", errsCount, N.errors, (i) => {
  60. gen.const(err, _`${N.vErrors}[${i}]`)
  61. gen.if(_`${err}.dataPath === undefined`, () =>
  62. gen.assign(_`${err}.dataPath`, strConcat(N.dataPath, it.errorPath))
  63. )
  64. gen.assign(_`${err}.schemaPath`, str`${it.errSchemaPath}/${keyword}`)
  65. if (it.opts.verbose) {
  66. gen.assign(_`${err}.schema`, schemaValue)
  67. gen.assign(_`${err}.data`, data)
  68. }
  69. })
  70. }
  71. function addError(gen: CodeGen, errObj: Code): void {
  72. const err = gen.const("err", errObj)
  73. gen.if(
  74. _`${N.vErrors} === null`,
  75. () => gen.assign(N.vErrors, _`[${err}]`),
  76. _`${N.vErrors}.push(${err})`
  77. )
  78. gen.code(_`${N.errors}++`)
  79. }
  80. function returnErrors(it: SchemaCxt, errs: Code): void {
  81. const {gen, validateName, schemaEnv} = it
  82. if (schemaEnv.$async) {
  83. gen.throw(_`new ${it.ValidationError as Name}(${errs})`)
  84. } else {
  85. gen.assign(_`${validateName}.errors`, errs)
  86. gen.return(false)
  87. }
  88. }
  89. const E = {
  90. keyword: new Name("keyword"),
  91. schemaPath: new Name("schemaPath"),
  92. params: new Name("params"),
  93. propertyName: new Name("propertyName"),
  94. message: new Name("message"),
  95. schema: new Name("schema"),
  96. parentSchema: new Name("parentSchema"),
  97. }
  98. function errorObjectCode(cxt: KeywordErrorCxt, error: KeywordErrorDefinition): Code {
  99. const {
  100. keyword,
  101. data,
  102. schemaValue,
  103. it: {gen, createErrors, topSchemaRef, schemaPath, errorPath, errSchemaPath, propertyName, opts},
  104. } = cxt
  105. if (createErrors === false) return _`{}`
  106. const {params, message} = error
  107. const keyValues: [Name, SafeExpr | string][] = [
  108. [E.keyword, keyword],
  109. [N.dataPath, strConcat(N.dataPath, errorPath)],
  110. [E.schemaPath, str`${errSchemaPath}/${keyword}`],
  111. [E.params, typeof params == "function" ? params(cxt) : params || _`{}`],
  112. ]
  113. if (propertyName) keyValues.push([E.propertyName, propertyName])
  114. if (opts.messages !== false) {
  115. const msg = typeof message == "function" ? message(cxt) : message
  116. keyValues.push([E.message, msg])
  117. }
  118. if (opts.verbose) {
  119. keyValues.push(
  120. [E.schema, schemaValue],
  121. [E.parentSchema, _`${topSchemaRef}${schemaPath}`],
  122. [N.data, data]
  123. )
  124. }
  125. return gen.object(...keyValues)
  126. }