patternProperties.ts 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import type {CodeKeywordDefinition} from "../../types"
  2. import type {KeywordCxt} from "../../compile/validate"
  3. import {allSchemaProperties, usePattern} from "../code"
  4. import {_, not, Name} from "../../compile/codegen"
  5. import {alwaysValidSchema, checkStrictMode} from "../../compile/util"
  6. import {evaluatedPropsToName, Type} from "../../compile/util"
  7. import {AnySchema} from "../../types"
  8. const def: CodeKeywordDefinition = {
  9. keyword: "patternProperties",
  10. type: "object",
  11. schemaType: "object",
  12. code(cxt: KeywordCxt) {
  13. const {gen, schema, data, parentSchema, it} = cxt
  14. const {opts} = it
  15. const patterns = allSchemaProperties(schema)
  16. const alwaysValidPatterns = patterns.filter((p) =>
  17. alwaysValidSchema(it, schema[p] as AnySchema)
  18. )
  19. if (
  20. patterns.length === 0 ||
  21. (alwaysValidPatterns.length === patterns.length &&
  22. (!it.opts.unevaluated || it.props === true))
  23. ) {
  24. return
  25. }
  26. const checkProperties =
  27. opts.strictSchema && !opts.allowMatchingProperties && parentSchema.properties
  28. const valid = gen.name("valid")
  29. if (it.props !== true && !(it.props instanceof Name)) {
  30. it.props = evaluatedPropsToName(gen, it.props)
  31. }
  32. const {props} = it
  33. validatePatternProperties()
  34. function validatePatternProperties(): void {
  35. for (const pat of patterns) {
  36. if (checkProperties) checkMatchingProperties(pat)
  37. if (it.allErrors) {
  38. validateProperties(pat)
  39. } else {
  40. gen.var(valid, true) // TODO var
  41. validateProperties(pat)
  42. gen.if(valid)
  43. }
  44. }
  45. }
  46. function checkMatchingProperties(pat: string): void {
  47. for (const prop in checkProperties) {
  48. if (new RegExp(pat).test(prop)) {
  49. checkStrictMode(
  50. it,
  51. `property ${prop} matches pattern ${pat} (use allowMatchingProperties)`
  52. )
  53. }
  54. }
  55. }
  56. function validateProperties(pat: string): void {
  57. gen.forIn("key", data, (key) => {
  58. gen.if(_`${usePattern(cxt, pat)}.test(${key})`, () => {
  59. const alwaysValid = alwaysValidPatterns.includes(pat)
  60. if (!alwaysValid) {
  61. cxt.subschema(
  62. {
  63. keyword: "patternProperties",
  64. schemaProp: pat,
  65. dataProp: key,
  66. dataPropType: Type.Str,
  67. },
  68. valid
  69. )
  70. }
  71. if (it.opts.unevaluated && props !== true) {
  72. gen.assign(_`${props}[${key}]`, true)
  73. } else if (!alwaysValid && !it.allErrors) {
  74. // can short-circuit if `unevaluatedProperties` is not supported (opts.next === false)
  75. // or if all properties were evaluated (props === true)
  76. gen.if(not(valid), () => gen.break())
  77. }
  78. })
  79. })
  80. }
  81. },
  82. }
  83. export default def