deepProperties.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
  1. import type {MacroKeywordDefinition, SchemaObject, Schema} from "ajv"
  2. import type {DefinitionOptions} from "./_types"
  3. import {metaSchemaRef} from "./_util"
  4. export default function getDef(opts?: DefinitionOptions): MacroKeywordDefinition {
  5. return {
  6. keyword: "deepProperties",
  7. type: "object",
  8. schemaType: "object",
  9. macro: function (schema: Record<string, SchemaObject>) {
  10. const allOf = []
  11. for (const pointer in schema) allOf.push(getSchema(pointer, schema[pointer]))
  12. return {allOf}
  13. },
  14. metaSchema: {
  15. type: "object",
  16. propertyNames: {type: "string", format: "json-pointer"},
  17. additionalProperties: metaSchemaRef(opts),
  18. },
  19. }
  20. }
  21. function getSchema(jsonPointer: string, schema: SchemaObject): SchemaObject {
  22. const segments = jsonPointer.split("/")
  23. const rootSchema: SchemaObject = {}
  24. let pointerSchema: SchemaObject = rootSchema
  25. for (let i = 1; i < segments.length; i++) {
  26. let segment: string = segments[i]
  27. const isLast = i === segments.length - 1
  28. segment = unescapeJsonPointer(segment)
  29. const properties: Record<string, Schema> = (pointerSchema.properties = {})
  30. let items: SchemaObject[] | undefined
  31. if (/[0-9]+/.test(segment)) {
  32. let count = +segment
  33. items = pointerSchema.items = []
  34. pointerSchema.type = ["object", "array"]
  35. while (count--) items.push({})
  36. } else {
  37. pointerSchema.type = "object"
  38. }
  39. pointerSchema = isLast ? schema : {}
  40. properties[segment] = pointerSchema
  41. if (items) items.push(pointerSchema)
  42. }
  43. return rootSchema
  44. }
  45. function unescapeJsonPointer(str: string): string {
  46. return str.replace(/~1/g, "/").replace(/~0/g, "~")
  47. }
  48. module.exports = getDef