ref.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import type {CodeKeywordDefinition, AnySchemaObject} from "../../types"
  2. import type {KeywordCxt} from "../../compile/validate"
  3. import {compileSchema, SchemaEnv} from "../../compile"
  4. import {_, not, nil, stringify} from "../../compile/codegen"
  5. import MissingRefError from "../../compile/ref_error"
  6. import N from "../../compile/names"
  7. import {getValidate, callRef} from "../core/ref"
  8. import {checkMetadata} from "./metadata"
  9. const def: CodeKeywordDefinition = {
  10. keyword: "ref",
  11. schemaType: "string",
  12. code(cxt: KeywordCxt) {
  13. checkMetadata(cxt)
  14. const {gen, data, schema: ref, parentSchema, it} = cxt
  15. const {
  16. schemaEnv: {root},
  17. } = it
  18. const valid = gen.name("valid")
  19. if (parentSchema.nullable) {
  20. gen.var(valid, _`${data} === null`)
  21. gen.if(not(valid), validateJtdRef)
  22. } else {
  23. gen.var(valid, false)
  24. validateJtdRef()
  25. }
  26. cxt.ok(valid)
  27. function validateJtdRef(): void {
  28. const refSchema = (root.schema as AnySchemaObject).definitions?.[ref]
  29. if (!refSchema) {
  30. throw new MissingRefError(it.opts.uriResolver, "", ref, `No definition ${ref}`)
  31. }
  32. if (hasRef(refSchema) || !it.opts.inlineRefs) callValidate(refSchema)
  33. else inlineRefSchema(refSchema)
  34. }
  35. function callValidate(schema: AnySchemaObject): void {
  36. const sch = compileSchema.call(
  37. it.self,
  38. new SchemaEnv({schema, root, schemaPath: `/definitions/${ref}`})
  39. )
  40. const v = getValidate(cxt, sch)
  41. const errsCount = gen.const("_errs", N.errors)
  42. callRef(cxt, v, sch, sch.$async)
  43. gen.assign(valid, _`${errsCount} === ${N.errors}`)
  44. }
  45. function inlineRefSchema(schema: AnySchemaObject): void {
  46. const schName = gen.scopeValue(
  47. "schema",
  48. it.opts.code.source === true ? {ref: schema, code: stringify(schema)} : {ref: schema}
  49. )
  50. cxt.subschema(
  51. {
  52. schema,
  53. dataTypes: [],
  54. schemaPath: nil,
  55. topSchemaRef: schName,
  56. errSchemaPath: `/definitions/${ref}`,
  57. },
  58. valid
  59. )
  60. }
  61. },
  62. }
  63. export function hasRef(schema: AnySchemaObject): boolean {
  64. for (const key in schema) {
  65. let sch: AnySchemaObject
  66. if (key === "ref" || (typeof (sch = schema[key]) == "object" && hasRef(sch))) return true
  67. }
  68. return false
  69. }
  70. export default def