required.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import type {CodeKeywordDefinition, ErrorObject, KeywordErrorDefinition} from "../../types"
  2. import type KeywordCxt from "../../compile/context"
  3. import {
  4. checkReportMissingProp,
  5. checkMissingProp,
  6. reportMissingProp,
  7. propertyInData,
  8. noPropertyInData,
  9. } from "../code"
  10. import {_, str, nil, not, Name, Code} from "../../compile/codegen"
  11. export type RequiredError = ErrorObject<
  12. "required",
  13. {missingProperty: string},
  14. string[] | {$data: string}
  15. >
  16. const error: KeywordErrorDefinition = {
  17. message: ({params: {missingProperty}}) => str`should have required property '${missingProperty}'`,
  18. params: ({params: {missingProperty}}) => _`{missingProperty: ${missingProperty}}`,
  19. }
  20. const def: CodeKeywordDefinition = {
  21. keyword: "required",
  22. type: "object",
  23. schemaType: "array",
  24. $data: true,
  25. error,
  26. code(cxt: KeywordCxt) {
  27. const {gen, schema, schemaCode, data, $data, it} = cxt
  28. const {opts} = it
  29. if (!$data && schema.length === 0) return
  30. const useLoop = schema.length >= opts.loopRequired
  31. if (it.allErrors) allErrorsMode()
  32. else exitOnErrorMode()
  33. function allErrorsMode(): void {
  34. if (useLoop || $data) {
  35. cxt.block$data(nil, loopAllRequired)
  36. } else {
  37. for (const prop of schema) {
  38. checkReportMissingProp(cxt, prop)
  39. }
  40. }
  41. }
  42. function exitOnErrorMode(): void {
  43. const missing = gen.let("missing")
  44. if (useLoop || $data) {
  45. const valid = gen.let("valid", true)
  46. cxt.block$data(valid, () => loopUntilMissing(missing, valid))
  47. cxt.ok(valid)
  48. } else {
  49. gen.if(checkMissingProp(cxt, schema, missing))
  50. reportMissingProp(cxt, missing)
  51. gen.else()
  52. }
  53. }
  54. function loopAllRequired(): void {
  55. gen.forOf("prop", schemaCode as Code, (prop) => {
  56. cxt.setParams({missingProperty: prop})
  57. gen.if(noPropertyInData(data, prop, opts.ownProperties), () => cxt.error())
  58. })
  59. }
  60. function loopUntilMissing(missing: Name, valid: Name): void {
  61. cxt.setParams({missingProperty: missing})
  62. gen.forOf(
  63. missing,
  64. schemaCode as Code,
  65. () => {
  66. gen.assign(valid, propertyInData(data, missing, opts.ownProperties))
  67. gen.if(not(valid), () => {
  68. cxt.error()
  69. gen.break()
  70. })
  71. },
  72. nil
  73. )
  74. }
  75. },
  76. }
  77. export default def